Skip to content

Conversation

@HansMuller
Copy link
Contributor

Enable a widget to inherit from a widget that's not its parent.

The widget contained by an InheritedWidgetLinkChild inherits from the ancestors of the InheritedWidgetLinkParent that it is linked to.

The link itself is bi-directional and must be defined in terms of global keys. The value of the InheritedWidgetLinkParent's link is the InheritedWidgetLinkChild's global key, and vice versa.

The inheritance link is exlusive: the InheritedWidgetLinkChild only inherits from the InheritedWidgetLinkParent's ancestors, it no longer inherits from its own ancestors.

The value of the link may be null, which restores normal inherited widget behavior.

Fixes #5572

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs please

@abarth
Copy link
Contributor

abarth commented Sep 26, 2016

Can you test what happens if you create a cycle (e.g., the child above the parent)?

@HansMuller HansMuller force-pushed the inherited_widget_link branch from 274f4a4 to a577c02 Compare September 26, 2016 22:09
@HansMuller
Copy link
Contributor Author

I've added asserts, docs, and more tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally these classes would be outside the framework.dart file. Is that possible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I've moved them into widgets/inherited_link.dart

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GlobalKey key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and oops.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would be redundant with the aforementioned change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and done.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use is! rather than !( is )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar comments here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this if block

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in fact in general you should see if this function needs updating; markNeedsBuild changed a bit recently

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow this is quite expensive. is there no quicker way to do this?

@Hixie
Copy link
Contributor

Hixie commented Sep 26, 2016

I suspect this doesn't work with LayoutBuilders. You should make sure you have tests where you have a layout builder and you change nothing (i.e. widgets are == across pumps) except that the layout builder is dirtied and something that would be inherited is dirtied, with situations like

      / ... -> layout builder -> ... -> inherited -> ... link parent 
root <
      \ ... -> link child -> ... -> inheritor
      / ... -> link child -> ... -> layout builder -> ... -> inheritor
root <
      \ ... -> inherited -> ... -> link parent 

Note that in these cases, the ... bits are important. They must be widgets that do not change identity or get dirtied in any way across pumps.

@Hixie
Copy link
Contributor

Hixie commented Sep 26, 2016

another couple of things to test:

      / link child -> inheritor
root <
      \ ... -> inherited -> ... -> link parent 
      / link child -> inheritor
root <
      \ ... -> layout builder -> ... -> inherited -> ... link parent 

...where the root and link child do get rebuilt (because you provide new, albeit unchanged, widgets in the second pump).

@HansMuller HansMuller force-pushed the inherited_widget_link branch from ad7c7ce to 5eaa842 Compare September 27, 2016 17:59
@HansMuller HansMuller force-pushed the inherited_widget_link branch from 5eaa842 to 3801dc2 Compare September 27, 2016 21:21
return new InkWell(
onTap: () { showButtonMenu(context); },
child: config.child

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you could assert that button is not null.

}
}

/// An element that uses a [InheritedWidgetLinkParent] as its configuration.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'a' -> 'an'

@override
InheritedWidgetLinkParent get widget => super.widget;

InheritedElementLinkChild get link => widget.link?._currentElement;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need docs?

final Element linkElement = widget.link?._currentElement;
if (linkElement != null && linkElement is! InheritedElementLinkChild) {
throw new FlutterError(
'InheritedWidgetLinkParent link value is not an InheritedWidgetLinkChild.\n'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can print the runtime type in the error.

@override
InheritedWidgetLinkChildProxy get widget => super.widget;

InheritedElementLinkParent get link => widget.link?._currentElement;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs?

final Element linkElement = widget.link?._currentElement;
if (linkElement != null && linkElement is! InheritedElementLinkParent) {
throw new FlutterError(
'InheritedWidgetLinkChild link value is not an InheritedWidgetLinkParent.\n'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Runtime type here as well.

@HansMuller
Copy link
Contributor Author

The last version of this code passed all of the proposed tests (and then some) except:

      / link child -> inheritor
root <
      \ ... -> layout builder -> ... -> inherited -> ... link parent 

In this case both halves of the link are built at layout time and the child is built first. That fails of course, because the child's parent and the values it's supposed to inherit aren't available. It's not possible to defer building the child until the parent has been built, since layout allocates sizes top-down.

The link child's implementation could report a zero size and notify the link parent that when it was laid out it should mark the child as needing to be rebuilt. This would cause the entire tree to be built again. Additional links could cause even more rebuilding which would kill performance.

The general purpose inheritance link is also flawed because in most cases one does not want the link child to inherit all of the link parent's inherited widgets. For example it would be a mistake to pick up the parent's TickerMode.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 15, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pesto in dark theme: menu text has wrong color

4 participants