Giter VIP home page Giter VIP logo

flutter_portal's Introduction

Build pub package codecov

flutter_portal: Evolved Overlay/OverlayEntry - declarative not imperative, intuitive-context, and easy-alignment

Want to show floating overlays - tooltips, contextual menus, dialogs, bubbles, etc? This library is an enhancement and replacement to Flutter's built-in Overlay/OverlayEntry.

๐Ÿš€ Advantages

Why using flutter_portal instead of built-in Overlay/OverlayEntry/OverlayPortal?

  • Declarative, not imperative: Like everything else in the Flutter world, overlays (portals) are declarative now. Simply put your floating UI in the normal widget tree. Compare: The OverlayEntry is notย a widget, and is manipulated imperatively using .insert()ย etc.
  • Alignment, done easily: Built-in support for aligning an overlay next to a UI component. Compare: A custom contextual menu from scratch in a few lines of code; while Overlay makes it nontrivial to align the tooltip/menu next to a widget.
  • Customizable alignment logic: For example, ensure the portal target never renders outside the screen (shiftToWithinBound), align it to portal instead of parent widget (alignToPortal), and you can even create your own align algorithm (extend EnhancedCompositedTransformAnchor). Compare: Overlay does not seem to have this.
  • The intuitive Context: The overlay entry is build with its intuitive parent as its context. Compare The Overlay approach uses the far-away overlay as its context. Update: OverlayPortal, which is inspired by this package, improved and did this well.

As a consequence, also have the following pros:

  • Easy restorable property: Since showing an overlay as simple as doing a setState, RestorableProperty works nicely. Compare: When using the Overlay approach, the state of our modals are not restored when our application is killed by the OS.
  • Correct Theme/provider: Since the overlay entry has the intuitive context, it has access to the same Theme and the different providers as the widget that shows the overlay. Compare: The Overlay approach will yield confusing Themes and providers. Update: OverlayPortal, which is inspired by this package, improved and did this well.

๐Ÿ‘€ Show me the code

PortalTarget(
  // 1. Declarative: Just provide `portalFollower` as a normal widget
  // 2. Intuitive BuildContext inside
  portalFollower: MyAwesomeOverlayWidget(),
  // 3. Align the "follower" relative to the "child" anywhere you like
  anchor: Aligned.center,
  child: MyChildWidget(),
)

To migrate from 0.x to 1.x, see the last section of the readme.

๐Ÿชœ Examples

Check-out the examples folder for examples on how to use flutter_portal:

Partial screenshots:

Contextual menu Onboarding view
Discovery example

๐Ÿงญ Usage

  1. Install it. Follow the standard procedure of installing this package. The simplest way may be flutter pub add flutter_portal.
  2. Add the Portal widget. For example, place it above MaterialApp. Only one Portal is needed per app.
  3. Use PortalTargets whenever you want to show some overlays.

๐Ÿ“š Tutorial: Show a contextual menu

In this example, we will see how we can use flutter_portal to show a menu after clicking on a RaisedButton.

Add the Portal widget

Before doing anything, you must insert the Portal widget in your widget tree. The follower widgets will behave as if they are inserted as children of this widget.

You can place this Portal above MaterialApp or near the root of a route:

Portal(
  child: MaterialApp(...)
)

The button

First, we need to create a StatefulWidget that renders our RaisedButton:

class MenuExample extends StatefulWidget {
  @override
  _MenuExampleState createState() => _MenuExampleState();
}

class _MenuExampleState extends State<MenuExample> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          onPressed: () {},
          child: Text('show menu'),
        ),
      ),
    );
  }
}

image

The menu - initial iteration

Then, we need to insert our PortalTarget in the widget tree.

We want our contextual menu to render right next to our RaisedButton. As such, our PortalTarget should be the parent of RaisedButton like so:

child: PortalTarget(
  visible: // TODO
  anchor: // TODO
  portalFollower: // TODO
  child: RaisedButton(...),
),

We can pass our menu to PortalTarget:

PortalTarget(
  visible: true,
  anchor: Filled(),
  portalFollower: Material(
    elevation: 8,
    child: IntrinsicWidth(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          ListTile(title: Text('option 1')),
          ListTile(title: Text('option 2')),
        ],
      ),
    ),
  ),
  child: RaisedButton(...),
)

At this stage, you may notice two things:

  • our menu is full-screen (because anchor is Filled)
  • our menu is always visible (because visible is true)

Change alignment

Let's fix the full-screen issue first and change our code so that our menu renders on the right of our RaisedButton.

To align our menu around our button, we can change the anchor parameter:

PortalTarget(
  visible: true,
  anchor: const Aligned(
    follower: Alignment.topLeft,
    target: Alignment.topRight,
  ),
  portalFollower: Material(...),
  child: RaisedButton(...),
)

What this code means is, this will align the top-left of our menu with the top-right or the `RaisedButton`. With this, our menu is no-longer full-screen and is now located to the right of our button.

Show the menu

Finally, we can update our code such that the menu show only when clicking on the button.

To do that, we need to declare a new boolean inside our StatefulWidget, that says whether the menu is open or not:

class _MenuExampleState extends State<MenuExample> {
  bool isMenuOpen = false;
  ...
}

We then pass this isMenuOpen variable to our PortalEntry:

PortalTarget(
  visible: isMenuOpen,
  ...
)

Then, inside the onPressed callback of our RaisedButton, we can update this isMenuOpen variable:

RaisedButton(
  onPressed: () {
    setState(() {
      isMenuOpen = true;
    });
  },
  child: Text('show menu'),
),

Hide the menu

One final step is to close the menu when the user clicks randomly outside of the menu.

This can be implemented with a second PortalEntry combined with [GestureDetector] like so:

PortalTarget(
  visible: isMenuOpen,
  portalFollower: GestureDetector(
    behavior: HitTestBehavior.opaque,
    onTap: () {
      setState(() {
        isMenuOpen = false;
      });
    },
  ),
  ...
),

๐ŸŽผ Concepts

There are a few concepts that are useful to fully understand when using flutter_portal. That is especially true if you want to support custom use cases, which is easily possible with the abstract API provided.

In the following, each of the abstract concepts you need to understand are explained on a high level. You will find them both in class names (e.g. the Portal widget or the PortalTarget widget as well as in parameter names).

Portal

A portal (or the portal if you only have one) is the space used for doing all of the portal work. On a low level, this means that you have one widget that allows its subtree to place targets and followers that are connected.

The portal also defines the area (rectangle bounds) that are available to any followers to be rendered onto the screen.

In detail, you might wrap your whole MaterialApp in a single Portal widget, which would mean that you can use the whole area of your app to render followers attached to targets that are children of the Portal widget.

Target

A target is any place within a portal that can be followed by a follower. This allows you to attach whatever you want to overlay to a specific place in your UI, no matter where it moves dynamically.

On a low level, this means that you wrap the part of your UI that you want to follow in a PortalTarget widget and configure it.

Example

Imagine you want to display tooltips when an avatar is hovered in your app. In that case, the avatar would be the portal target and could be used to anchor the tooltip that is overlayed.

Another example would be a dropdown menu. The widget that shows the current selection is the target and when tapping on it, the dropdown options would be overlayed through the portal as the follower.

Follower

A follower can only be used in combination with a target. You can use it for anything that you want to overlay on top of your UI, attached to a target.

Specifically, this means that you can pass one follower to every PortalTarget, which will be displayed above your UI within the portal when you specify so.

Example

If you wanted to display an autocomplete text field using flutter_portal, you would want to follow the text field to overlay your autocomplete suggestions. The widget for the autocomplete suggestions would be the portal follower in that case.

Anchor

Anchors define the layout connection between targets and followers. In general, anchors are implemented as an abstract API that provides all the information necessary to support any positioning you want. That means that anchors can be defined based on the attributes of the associated portal, target, and follower.

There are a few anchors that are implemented by default, e.g. Aligned or Filled.

โ›ต Migration from 0.x

There are some breaking changes (mostly introduced by #44) from 0.x to 1.0, but it can be easily migrated. The following:

PortalEntry(
  portalAnchor: Alignment.topLeft,
  childAnchor: Alignment.topRight,
  portal: MyAwesomePortalWidget(),
  child: MyAwesomeChildWidget(),
)

Becomes:

PortalTarget(
  anchor: const Aligned(
    follower: Alignment.topLeft,
    target: Alignment.topRight,
  ),
  portalFollower: MyAwesomePortalWidget(),
  child: MyAwesomeChildWidget(),
)

If you originally use PortalEntry without portalAnchor/childAnchor (i.e. make it fullscreen), then you can write as:

PortalTarget(
  anchor: const Filled(),
  ...
)

โœจ Acknowledgement

Owners

  • @rrousselGit: The former owner of this package. Create this package in December 2019, and majorly maintain until early 2022. Contributions include: Implementation of the package, including code, documentations, examples, etc. Change algorithms of rendering. Remove PortalEntry's generic. Allow delaying the disappearance of PortalEntry, useful for leave animations.
  • @fzyzcjy: The current owner of this package. See CHANGELOG.md for contributions.

Contributors

flutter_portal's People

Contributors

caseyhillers avatar creativecreatorormaybenot avatar fzyzcjy avatar hansmuller avatar jjagg avatar mityax avatar mono0926 avatar mufaddal1125 avatar nilsreichardt avatar rrousselgit avatar srawlins avatar venkatd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flutter_portal's Issues

Discovery example: rendering error in the FloatingActionButton

Describe the bug
If I run example/lib/discovery.dart, the FloatingActionButton does not render correctly when the PortalEntry is visible: a tiny one is rendered on top of a genuine-sized one (and this one falls below the Barrier):

PortalScreenshotNoDiscovery

PortalScreenshotWithDiscovery

To Reproduce
Run https://github.com/rrousselGit/flutter_portal/blob/master/example/lib/discovery.dart

Expected behavior
The FloatingActionButton should be displayed full size.

Flutter version
Flutter 1.22.3 โ€ข channel stable โ€ข https://github.com/flutter/flutter.git
Framework โ€ข revision 8874f21e79 (11 days ago) โ€ข 2020-10-29 14:14:35 -0700
Engine โ€ข revision a1440ca392
Tools โ€ข Dart 2.10.3

Is there a way to position the Portal above all?

I need the overlay widget be on top of all other widgets in the app, no matter what navigator state the app is in.
I want to change the portal widget through provider
Is this possible, if it is, where should I put the portal code in?

(Willing to make a PR) Cannot control relative "z-index" among multiple portals

Hi thanks so much for this helpful lib! I need to control relative "z-index" among multiple portals. For example, Portal A is created in one piece of my code, and Portal B is created very far away in another piece of my code. Then I want portal A to be "above" B (i.e. have larger z-index). But seems that I cannot control this. Any help is appreciated!

Navigator pop after entry click?

Describe the bug
After click, all current page is popped instead of only portal. I am doing something wrong?

To Reproduce
Reproduced code is in your own example contextual_menu. After you click on the first or second item of 'show menu' button, screen is disposed.

Expected behavior
Only the portal menu should be closed.

Click not working when nesting Portal inside a Row

a failing test:

testWidgets(
    'regression test for Row+expanded',
    (tester) async {
      var didClick = false;

      await tester.pumpWidget(
        Boilerplate(
          child: Row(
            children: [
              SizedBox(width: 70),
              // Expanded(child: Container(color: Colors.red)),
              LimitedBox(
                maxWidth: 300,
                child: Portal(
                  child: PortalEntry(
                    portalAnchor: Alignment.center,
                    childAnchor: Alignment.center,
                    portal: SizedBox.expand(
                      child: RaisedButton(
                        onPressed: () => didClick = true,
                      ),
                    ),
                    child: Container(
                      height: 200,
                      width: double.infinity,
                      color: Colors.blue,
                    ),
                  ),
                ),
              )
            ],
          ),
        ),
      );

      print(MediaQuery.of(tester.element(find.byType(Scaffold))).size);

      print(
          'x ${tester.getCenter(find.byType(RaisedButton)).dx} y ${tester.getCenter(find.byType(RaisedButton)).dy}');

      await tester.tapAt(tester.getTopLeft(find.byType(RaisedButton)));

      await expectLater(
        find.byType(Boilerplate),
        matchesGoldenFile('row_expanded.png'),
      );

      expect(didClick, isTrue);
    },
  );

Let portal UI won't out of screen

This is a library which used flutter portal.
fayeed/flutter_mentions#10

  1. Can the flutter portal provide a way can let the portal UI always in the screen?
  2. Or, let the portal UI be like a magnet in a case, just always around the main UI but won't follow the UI out of screen.

Portal follower with Filled anchor does not resize correctly when a surrounding Column changes height. [on master]

Describe the bug
When a PortalTarget is inside a Column (not with Expanded etc.), which is surrounded by the Portal and the anchor ist set to Filled, the portalFollower does not change its height to match the Portal if the Portal changes height. However it does update when the width is changed.

To Reproduce

  • Create a new project flutter create flutter_portal_bug
  • Add the following dependency in the pubspec.yaml
  flutter_portal:
    git:
      url: https://github.com/rrousselGit/flutter_portal
      ref: 682cc5ef459dfeb8d102eecd9e2ddbb3252d984e
  • Replace the main.dart with the following content:
import 'package:flutter/material.dart';
import 'package:flutter_portal/flutter_portal.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'flutter_portal bug',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  double width = 64;
  double height = 64;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Slider(
            value: width,
            min: 0,
            max: 512,
            onChanged: (v) => setState(() => width = v),
          ),
          Slider(
            value: height,
            min: 0,
            max: 512,
            onChanged: (v) => setState(() => height = v),
          ),
          Expanded(
            child: Center(
              child: SizedBox(
                width: width,
                height: height,
                child: Portal(
                  child: Container(
                    color: Colors.black,
                    child: Column(
                      children: [
                        PortalTarget(
                          portalFollower: Container(
                            color: Colors.blue.withOpacity(0.5),
                          ),
                          child: Container(
                            color: Colors.blueGrey,
                            width: 24,
                            height: 24,
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
  • Run the App
  • There are two sliders on the top. The upper one controlls the width of the Portal, and the lower one the height. The Portal has a black container as child which indicates the size of the Portal. The portalFollower is a semi transparent blue container.

Expected behavior

  • The transparent blue container (portalTarget) always covers the black container (Portal) since the anchor is implicitly set to Filled

Actual behavior

  • The transparent blue container (portalTarget) only changes its size to fit the black container (Portal) when the width is changed, but not when the height is changed.
  • When the inner Column is remove, everything behaves as expected.

Include package tests in `flutter/tests`

As proposed in flutter/flutter#99349 (comment) and in flutter/flutter#92598 (comment), I created a PR to add flutter_portal to flutter/tests#126.

Looking at the existing tests for the package and the ones I added in #44, this should comply with all of the rules for flutter/tests (as defined in the README, see https://github.com/flutter/tests).

One concern there is is the fact that I added myself as the contact (see https://github.com/flutter/tests/pull/126/files#diff-b02b94965c78db569a2cf4abb644a108bdad643e5e1c664d1ac69a5aa21931d0R1).
I am willing to follow-up on any requests to comply with breaking changes, however, I do not have write access to this repo (cc @rrousselGit @fzyzcjy). We would either have to change that or (maybe preferably) include one of the two of you as the contact in flutter/tests.

Using anchoring together with Positioned.fill

Is it possible to create a PortalEntry that has the following two behaviors at the same time:

  • full screen GestureDetector to dismiss the current PortalEntry whenever you tap outside of it (like your date picker example)
  • anchoring the actual overlay Widget to the child (like in the contextual menu example)

I tried various things, even using two PortalEntries at the same time (one full screen for the GestureDetector and another with just the Widget that gets anchored to the child), but couldn't get it to work and am wondering whether this is actually possible with the current design.

Previously, I implemented my Overlays by hand with a Stack, but then I failed to position the overlay correctly, because I don't have the size of the actual overlay when it comes to positioning it relative to the target with CompositedTransformFollower. I hoped your library would be a solution to this, but at the moment I am failing to achieve the points above.

Support PortalEntry display priorities

I use the portal to implement a widget that is displayed only when hovering, and when I right-click on the widget I need to display the context menu, but the context menu is displayed at a level below the hovering widget.

I would like to be able to configure the priority.

Necessity to provide a top level Portal

I was thinking about replacing my current implementation of a context menu based on OverlayEntry with PortalEntry.

Currently, I have a widget tree like this:

MaterialApp -> Homescreen -> Scaffold -> CustomScrollView -> Column -> Row -> CustomButton

CustomButton is where I currently implemented showing the OverlayEntry. When switching to using PortalEntry, I have to wrap some outer widget like CustomScrollView with Portal.
If I only use Portal to wrap CustomButton's build method, full screen overlays are offset to the bottom right (i.e. they do not fill the screen anymore) and hit testing on the Portal does not seem to work (at least when the Portal is positioned to the top left of CustomButton).

Is it possible to use PortalEntry without wrapping the upper widget tree in a Portal? It feels weird to wrap the outer widgets, since the choice of using PortalEntry over OverlayEntry should be an implementation detail of my CustomButton widget and not affect the parent widgets. Could we maybe add a variable like useRootOverlay that always assumes a full screen Portal at 0x0 (while still allowing anchoring it to its child) or maybe even do that as default if no parent Portal is specified?

'_debugCheckLeaderBeforeFollower(forwardLayers, inverseLayers)': LeaderLayer anchor must come before FollowerLayer in paint order, but the reverse was true.

Digging out the issue now.

Have not reduce the real-world use case into a minimal reproducible sample yet.

`'package:flutter_portal/src/flutter_src/rendering_layer.dart': Failed assertion: line 365 pos 7: '_debugCheckLeaderBeforeFollower(forwardLayers, inverseLayers)': LeaderLayer anchor must come before FollowerLayer in paint order, but the reverse was true.`


flutter: [extra#0] bound_extra.0 => FlutterError.onError raw stackTrace=#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
flutter: #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
flutter: #2      CustomFollowerLayer._establishTransform (package:flutter_portal/src/flutter_src/rendering_layer.dart:365:7)
flutter: #3      CustomFollowerLayer.addToScene (package:flutter_portal/src/flutter_src/rendering_layer.dart:404:5)
flutter: #4      Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:545:5)
flutter: #5      ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1105:13)
flutter: #6      CustomLeaderLayer.addToScene (package:flutter_portal/src/flutter_src/rendering_layer.dart:140:5)
flutter: #7      Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:545:5)
flutter: #8      ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1105:13)
flutter: #9      CustomLeaderLayer.addToScene (package:flutter_portal/src/flutter_src/rendering_layer.dart:140:5)
flutter: #10     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:545:5)
flutter: #11     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1105:13)
flutter: #12     CustomLeaderLayer.addToScene (package:flutter_portal/src/flutter_src/rendering_layer.dart:140:5)
flutter: #13     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:545:5)
flutter: #14     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1105:13)

docs

The docs in the readme are top notch - great job.

Seriously looking at using this but wondring if it uses the new Flutter Navigator 2.0 API.
I read that google are going to support the old API also.

Just trying to work out how the puzzle fits together for our roadmap and if we shoudl use Portal.

One thing we find is that Modal Overlays dont always work well on Flutter Web for example.

`Failed assertion: '_lastOffset != null'` in various cases, which should exist in Flutter 2.8~2.10 and flutter_portal from old to new

Description and severety

I have a PortalTarget as a child widget of TextField (by custom rendering), then once in a while, this error raises.

Indeed I observed this as soon as in 2021 Oct (e.g. #15 (comment)) but did not fix that.

Before #44 and #47 is landed, this bug also exists and is even more severe: For example, in #15, portal-in-portal has trouble. The #47 unconsciously partially fixes this bug by replacing flutter 2.10's LeaderLayer with CustomLeaderLayer copied from flutter 2.11 beta.

Logs

debug build I see:

[sentry] [debug] Capture from onError 'package:flutter/src/rendering/layer.dart': Failed assertion: line 2284 pos 12: '_lastOffset != null': is not true.
[sentry.flutterError] [error] Exception caught by scheduler library
                      'package:flutter/src/rendering/layer.dart': Failed assertion: line 2284 pos 12: '_lastOffset != null': is not true.
                      #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
                      #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
                      #2      LeaderLayer.applyTransform (package:flutter/src/rendering/layer.dart:2284:12)
                      #3      CustomFollowerLayer._collectTransformForLayerChain (package:flutter_portal/src/flutter_src/rendering_layer.dart:258:22)
                      #4      CustomFollowerLayer._establishTransform (package:flutter_portal/src/flutter_src/rendering_layer.dart:357:30)
                      #5      CustomFollowerLayer.addToScene (package:flutter_portal/src/flutter_src/rendering_layer.dart:391:5)
                      #6      Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:545:5)
                      #7      ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1105:13)
                      #8      OffsetLayer.addToScene (package:flutter/src/rendering/layer.dart:1239:5)
                      #9      Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:545:5)
                      #10     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1105:13)
                      #11     TransformLayer.addToScene (package:flutter/src/rendering/layer.dart:1680:5)
                      #12     ContainerLayer.buildScene (package:flutter/src/rendering/layer.dart:937:5)
                      #13     RenderView.compositeFrame (package:flutter/src/rendering/view.dart:228:37)
                      #14     RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:501:18)
                      #15     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:883:13)
                      #16     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
                      #17     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
                      #18     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1081:9)
                      #19     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:995:5)
                      #20     _rootRun (dart:async/zone.dart:1426:13)
                      #21     _CustomZone.run (dart:async/zone.dart:1328:19)
                      #22     _CustomZone.runGuarded (dart:async/zone.dart:1236:7)
                      #23     _invoke (dart:ui/hooks.dart:151:10)
                      #24     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
                      #25     _drawFrame (dart:ui/hooks.dart:115:31)

release build I see:

_CastError: Null check operator used on a null value
  File "layer.dart", line 2286, in LeaderLayer.applyTransform
  File "rendering_layer.dart", line 258, in CustomFollowerLayer._collectTransformForLayerChain
  File "rendering_layer.dart", line 357, in CustomFollowerLayer._establishTransform
  File "rendering_layer.dart", line 391, in CustomFollowerLayer.addToScene
  File "layer.dart", line 545, in Layer._addToSceneWithRetainedRendering
  File "layer.dart", line 1105, in ContainerLayer.addChildrenToScene
  File "layer.dart", line 1239, in OffsetLayer.addToScene
  File "layer.dart", line 545, in Layer._addToSceneWithRetainedRendering
  File "layer.dart", line 1105, in ContainerLayer.addChildrenToScene
  File "layer.dart", line 1680, in TransformLayer.addToScene
  File "layer.dart", line 937, in ContainerLayer.buildScene
  File "view.dart", line 228, in RenderView.compositeFrame
  File "binding.dart", line 501, in RendererBinding.drawFrame
  File "binding.dart", line 883, in WidgetsBinding.drawFrame
  File "binding.dart", line 363, in RendererBinding._handlePersistentFrameCallback
  File "binding.dart", line 1144, in SchedulerBinding._invokeFrameCallback
  File "binding.dart", line 1081, in SchedulerBinding.handleDrawFrame
  File "binding.dart", line 995, in SchedulerBinding._handleDrawFrame
  File "zone.dart", line 1426, in _rootRun
  File "zone.dart", line 1328, in _CustomZone.run
  File "zone.dart", line 1236, in _CustomZone.runGuarded
  File "hooks.dart", line 151, in _invoke
  File "platform_dispatcher.dart", line 308, in PlatformDispatcher._drawFrame
  File "hooks.dart", line 115, in _drawFrame

tabaco-tv go

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Sometimes error `Cannot find followerParentUsedScopeIndex`

Example:

Exception: Cannot find followerParentUsedScopeIndex info=SanityCheckNestedPortalInfo{selfDebugLabel: for-TutorialHintBubble-annotated_mode.anchor_display, parentDebugLabel: for-CivAnchorInjectedWidget, selfScope: PortalLinkScope(portalLink: PortalLink#b379d, portalIdentifier: [String <'Tutorial'>])(hash=fd0fc), parentScope: PortalLinkScope(portalLink: PortalLink#6d1f3, portalIdentifier: [void ])(hash=e80ed), portalLinkScopeAncestors: [PortalLinkScope(portalLink: PortalLink#6d1f3, portalIdentifier: [void ])(hash=9fa70), PortalLinkScope(portalLink: PortalLink#b379d, portalIdentifier: [St
flutter: ring <'Tutorial'>])(hash=fd0fc), PortalLinkScope(portalLink: PortalLink#0958c, portalIdentifier: [void ])(hash=1f244)]}}

If `Portal` is placed inside `Scaffold`, then no clicking event will go to the portal entry

Using the following code, we can see: the "CHILD" can be tapped, but the "PORTAL" cannot be tapped.

image

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

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('hi')),
        body: Portal(
          child: Container(
            color: Colors.red.withAlpha(100),
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 32),
              child: PortalEntry(
                portalAnchor: Alignment.topCenter,
                childAnchor: Alignment.bottomCenter,
                portal: Material(
                  color: Colors.blue.withAlpha(100),
                  child: InkWell(
                    onTap: () => print('CHILD'),
                    child: Text('PORTAL'),
                  ),
                ),
                child: Material(
                  color: Colors.green.withAlpha(100),
                  child: InkWell(
                    onTap: () => print('CHILD'),
                    child: Text('CHILD'),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

However, if in the example above, we swap the Portal such that it is outside the Scaffold, then the tapping can be received normally.

Updating visible for nested portals.

When nesting two portal entries and defining the same visible for both , only the inner portal is updated.

PortalEntry(
      visible: isOpen,
      child: PortalEntry(
        visible: isOpen,
        ...
      ),
      ...
  );

Support for Positioned and AnimatedPositioned

The following code gives me Incorrect use of ParentDataWidget exception

PortalEntry(
     portal: Positioned(
          top: 24.0,
          left: 16.0,
          child: Card(
               child: Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Text("Hi, I'm portal."),
               ),
           ),
     ),
     child: Text(
          'Example',
     ),
)

same goes with AnimatedPositioned

I think it's better to have PortalEntry the same use case as the OverlayEntry where Positioned and AnimatedPositioned works.

btw, nice package, looking forward to further development. ๐Ÿ‘

Allow the follower partially follow the target in selected axis; allow align relative to `Portal`

Is your feature request related to a problem? Please describe.

I have widget A (the red one in demo image) in my widget tree somewhere. Then I want to create widget B (blue one) by the following rules: Its x should be the width of screen. Its y should be the same as widget A (red one).

I know how to fully follow another widget by using PortalEntry's childAnchor and portalAnchor, but do not know how to do it partially only on one dimension.

Image illustrating my question (just Photoshoped it, not a screenshot):

enter image description here

Thanks very much!

Describe the solution you'd like
N/A

Describe alternatives you've considered
N/A

Additional context
N/A

tabaco tv go

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

PortalEntry that binds to a ancestor but not nearest Portal

Say I have very simplified code like:

Portal( // #1
  child: Portal( // #2
    child: PortalEntry()))

I want that PortalEntry to be put to 1st portal instead of 2nd.

The real use case: portal 1 is high in the tree and portal 2 is much lower, with lots of widgets in between. for some style reasons I need to put in the first.

Related: https://stackoverflow.com/questions/71200969/dependoninheritedwidgetofexacttype-but-obtain-non-nearest-ancestor

Please document how hit-testing works

I tried to use flutter_portal to render "stickers" on top of "Cards", like this:

############
#          #
# Foo      #
# 1234   /-------------|    
#       <    Important |
#        \-------------|   
#          #
#          #
############

However, tapping on "Important" isn't able to receive tap gestures.

UPDATE: It looks like the hittest is happening vertically below the sticker?! If I tap like 100 pixels below "Important", it's getting tapped.

UPDATE: Looks like Portal has to be fullscreen?

Would be nice if a PortalEntry could self-contain it's own (always fullscreen) modal.

The current approach of using 2 sibling Widgets is not condusive to standalone components.

Commonly we want the same behavior for all ToolTips and inline popups which is a basic "showBarrier" and "barrierDismissable".

The current approach requires you to add an Extra PortalEntry to every single view that uses tooltips, and re-write the same logic over and over, or create some extra architectural layer to smooth out this pain pt. It also forces you to maintain multiple bools to track multiple popups, rather than just letting each popup encapsulate it's own state.

Would something like this be possible?

StatefulPortalEntry(
            key: tipKey,
            barrierColor: Colors.black.withOpacity(.1);
            barrierDismissable: false,
            childAnchor: Alignment.bottomLeft,
            portalAnchor: Alignment.topLeft,
            child: ...
...
tipKey.currentState.visible = true;

The basic goal is, if I have a component, that has some popup menu (Like UserAvatar), and I have it exist in various views, I don't have to constantly change the surrounding view to have the popover work. I want to be able to plop a UserAvatar in place, and it works, I don't think this is possible currently.

Add offset parameter to PortalEntry

Is your feature request related to a problem? Please describe.
If it is needed to offset the overlayed widget by a fixed amount, one way to do that currently is with a Transform widget. However there, one might run into issues with the limitations described in flutter/flutter#27587.
There are ways to circumvent this, for example by making the overlayed widget bigger and positioning the child within it with an offset. (For example by using a Stack.)

Describe the solution you'd like
However it would be more convenient to have an offset parameter in the PortalEntry that just offsets the portal child by a fixed amount.

Runtime exceptions on Flutter 1.22.0

PortalEntry causes some exceptions and it shows no overlaying widgets when using Flutter 1.22.0.

Stacktraces

Trying flutter_portal with example code https://github.com/rrousselGit/flutter_portal/blob/master/example/lib/date_picker.dart

โ•โ•โ•โ•โ•โ•โ•โ• Exception caught by widgets library โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
'package:flutter/src/widgets/framework.dart': Failed assertion: line 6131 pos 12: 'slot == null': is not true.
The relevant error-causing widget was: 
  PortalEntry file:///Users/kikuchy/tmp/flutter_portal/example/lib/date_picker.dart:24:12
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

slot looks like IndexedSlot of CompositedTransformTarget at here https://github.com/rrousselGit/flutter_portal/blob/master/lib/src/portal.dart#L178

And using https://github.com/rrousselGit/flutter_portal/blob/master/example/lib/date_picker.dart , there are one more exception.

โ•โ•โ•โ•โ•โ•โ•โ• Exception caught by widgets library โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
'package:flutter/src/widgets/framework.dart': Failed assertion: line 6131 pos 12: 'slot == null': is not true.
The relevant error-causing widget was: 
  LayoutBuilder file:///Users/kikuchy/tmp/flutter_portal/lib/src/portal.dart:183:18
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

โ•โ•โ•โ•โ•โ•โ•โ• Exception caught by rendering library โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
'package:flutter/src/widgets/layout_builder.dart': Failed assertion: line 161 pos 12: 'slot == null': is not true.
The relevant error-causing widget was: 
  LayoutBuilder file:///Users/kikuchy/tmp/flutter_portal/lib/src/portal.dart:183:18
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

โ•โ•โ•โ•โ•โ•โ•โ• Exception caught by rendering library โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
RenderBox was not laid out: _RenderLayoutBuilder#71d45 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1785 pos 12: 'hasSize'
The relevant error-causing widget was: 
  Stack file:///Users/kikuchy/tmp/flutter_portal/lib/src/portal.dart:176:12
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Result of Flutter doctor

โžœ  example git:(master) โœ— flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[โœ“] Flutter (Channel stable, 1.22.0, on Mac OS X 10.15.6 19G2021, locale ja-JP)
 
[โœ“] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
[โœ“] Xcode - develop for iOS and macOS (Xcode 12.0)
[โœ“] Android Studio (version 4.0)
[โœ“] IntelliJ IDEA Ultimate Edition (version 2020.2.2)
[โœ“] IntelliJ IDEA Community Edition (version 2019.2.3)
[!] VS Code (version 1.49.2)
    โœ— Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[โœ“] Connected device (1 available)

! Doctor found issues in 1 category.

Discovery example breaks on resize on desktop

Describe the bug
In certain conditions a full-size PortalEntry's size is not updated when resizing the window.

To Reproduce

  • Replace the discovery example's build method with this. I can't get it to reproduce with the PortalEntry around the floating action button, but around the other button it reproduces consistently.
 @override
  Widget build(BuildContext context) {
    return Portal(
      child: MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: const Text('Discovery example')),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text('You have clicked the button this many times:'),
                Text('$count', style: Theme.of(context).textTheme.headline4),
                Discovery(
                    visible: showDiscovery,
                    description: const Text('Click to increment the counter'),
                    onClose: () => setState(() => showDiscovery = false),
                    child: ElevatedButton(
                      onPressed: () => setState(() => showDiscovery = true),
                      child: const Text('Show discovery'),
                    )),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _increment,
            child: const Icon(Icons.add),
          ),
        ),
      ),
    );
  }
  • Generate the desktop files by running flutter create . in the examples folder with a desktop configuration enabled
  • Run the app
  • Resize the window, it's not super consistent, but it does happen more if you drag the bottom right corner of the window to the far right of the screen and then drag down a ways.

See the following:
Screen Shot 2022-02-17 at 11 15 22 PM

Expected behavior

The full-size PortalEntry overlay should resize like the rest of the window (which does update appropriately), the PortalEntry that follows the button is fine, just the barrier doesn't fill the space.

Tooltip is painted behind portal

Describe the bug
Icon tooltip is painted behind portal material.

image

To Reproduce

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

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

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

  @override
  Widget build(BuildContext context) {
    return Portal(
      child: MaterialApp(
        home: Scaffold(
          body: PortalEntry(
            visible: true,
            childAnchor: Alignment.center,
            portalAnchor: Alignment.center,
            portal: Material(
              color: Colors.white,
              child: SizedBox(
                width: 80,
                height: 80,
                child: IconButton(
                  icon: Icon(Icons.hot_tub),
                  tooltip: 'This tooltip is behind Material above',
                  onPressed: () {},
                ),
              ),
            ),
            child: Container(
              color: Colors.grey,
            ),
          ),
        ),
      ),
    );
  }
}

Expected behavior
Icon tooltip should be rendered on top.

It should be documented that the portal of a PortalEntry is build in the context of the PortalEntry not of the Portal

Describe what scenario you think is uncovered by the existing examples/articles
The widget defined in an OverlayEntry is built in the BuildContext of the surrounding Overlay.
However in the flutter_portal package, the portal parameter of the PortalEntry is built in the context of the PortalEntry and not (/only indirectly) of the surrounding Portal.
I think this change is good, however it should be reflected in the documentation because it might be confusing to some that are used to the Overlay system.

Additional context
Just out of curiosity. Can you explain how it is possible that the portal parameter of the PortalEntry is built in the context of the PortalEntry? Because If I understand correctly, in the render object tree, it is still a child of the Portal and not of the PortalEntry, but in the element tree, the portal is a child of the PortalEntry. It seems very illegal to have these trees "diverge" in such a way. I always thought, that the render object tree is a subtree of the element tree.

Visibility protection

Consider your contextual menu example...

if you change the position of the button from Center to Align(alignment: bottomCenter...) then the menu appears 1/2 off screen.
would be very nice if it's position were corrected to ensure it is completely visible.

Really nice library, but for me that's a show stopper, because the anchor widget positions are not guaranteed to be away from the edge of the screen (e.g. on phones).

README.md has wrong version

The Installation section of README.md specifies flutter_portal: ^0.0.1, but it should be flutter_portal: ^0.1.0.

The problem is that the 0.0.1 versions no longer build, and Upgrade Packages won't automatically upgrade ^0.0.1 to 0.1.0. That means that the initial experience that people have with this package will be disappointing.

UnimplementedError was thrown during paint

Bug
In the example app, the circle is not drawn around the Increment button.
flutter_portal_bug

To Reproduce
Build and Run the example app in web.

Desktop:

  • OS: Windows
  • Browser: Chrome
  • Version: 85.0.4183.121

Error:

โ•โ•โ•โ•โ•โ•โ•โ• Exception caught by rendering library โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
The following UnimplementedError was thrown during paint():
UnimplementedError

The relevant error-causing widget was: 
  CustomPaint file:///C:/Users/I524760/StudioProjects/flutter_portal/example/lib/discovery.dart:86:13
When the exception was thrown, this was the stack: 
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 212:49  throw_
C:/b/s/w/ir/cache/builder/src/out/host_debug/flutter_web_sdk/lib/ui/src/ui/path.dart 70:5                                  combine
packages/example/discovery.dart 140:12                                                                                     paint
packages/flutter/src/rendering/custom_paint.dart 531:12                                                                    [_paintWithPainter]
packages/flutter/src/rendering/custom_paint.dart 572:7                                                                     paint
...
The following RenderObject was being processed when the exception was fired: RenderCustomPaint#635c0 relayoutBoundary=up2
...  needs compositing
...  parentData: not positioned; offset=Offset(0.0, 0.0) (can use size)
...  constraints: BoxConstraints(0.0<=w<=1536.0, 0.0<=h<=722.4)
...  size: Size(156.0, 156.0)
RenderObject: RenderCustomPaint#635c0 relayoutBoundary=up2
  needs compositing
  parentData: not positioned; offset=Offset(0.0, 0.0) (can use size)
  constraints: BoxConstraints(0.0<=w<=1536.0, 0.0<=h<=722.4)
  size: Size(156.0, 156.0)
...  child: RenderPadding#558b3 relayoutBoundary=up3 NEEDS-PAINT
...    needs compositing
...    parentData: <none> (can use size)
...    constraints: BoxConstraints(0.0<=w<=1536.0, 0.0<=h<=722.4)
...    size: Size(156.0, 156.0)
...    padding: EdgeInsets.all(50.0)
...    textDirection: ltr
...    child: RenderMergeSemantics#45e8d relayoutBoundary=up4 NEEDS-PAINT
...      needs compositing
...      parentData: offset=Offset(50.0, 50.0) (can use size)
...      constraints: BoxConstraints(0.0<=w<=1436.0, 0.0<=h<=622.4)
...      semantic boundary
...      size: Size(56.0, 56.0)
...      child: RenderConstrainedBox#a5ab5 relayoutBoundary=up5 NEEDS-PAINT
...        needs compositing
...        parentData: <none> (can use size)
...        constraints: BoxConstraints(0.0<=w<=1436.0, 0.0<=h<=622.4)
...        size: Size(56.0, 56.0)
...        additionalConstraints: BoxConstraints(unconstrained)
...        child: RenderOffstage#e63b8 relayoutBoundary=up6 NEEDS-PAINT
...          needs compositing
...          parentData: <none> (can use size)
...          constraints: BoxConstraints(0.0<=w<=1436.0, 0.0<=h<=622.4)
...          size: Size(56.0, 56.0)
...          offstage: false
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Having trouble using a Portal inside of a Portal to recreate a 'Dialog' triggered from a 'Modal'

I was having a lot of fun creating custom Datepickers and Dropdown buttons for myself, which work great.
Then I made a kind of modal/form which is also a PortalEntry, which works great.
But my problem is when I try to use one of my Datepickers/Dropdowns inside of my Pop up form, I get the following:

LeaderLayer anchor must come before FollowerLayer in paint order, but the reverse was true.
'package:flutter/src/rendering/layer.dart':

I assume this is because I am putting the PortalEntry inside of a portal, not a PortalEntry child. But I am struggling to find a workaround.

I tried putting another portalEntry in the form child and then passing the next portal I want when the corresponding button is clicked in the form. But the portal passed shows up underneath the form.

I tried even just showing a regular Dialog from the form. But it shows up underneath.

Would really appreciate any tips. Also just want to say, awesome plugin, allows for some really nice custom widgets

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.