@@ -300,7 +300,6 @@ class FlexLayoutExplorerWidgetState
300300 ),
301301 ),
302302 child: VisualizeFlexChildren (
303- state: this ,
304303 properties: propertiesLocal,
305304 children: children,
306305 highlighted: highlighted,
@@ -383,14 +382,22 @@ class FlexLayoutExplorerWidgetState
383382 ),
384383 );
385384
386- return Container (
387- constraints: BoxConstraints (maxWidth: maxWidth, maxHeight: maxHeight),
388- child: Stack (
389- children: [
390- flexDescription,
391- verticalAxisDescription,
392- horizontalAxisDescription,
393- ],
385+ return _FlexLayoutExplorerScope (
386+ rootProperties: propertiesLocal,
387+ onTap: onTap,
388+ onDoubleTap: onDoubleTap,
389+ markAsDirty: markAsDirty,
390+ entranceController: entranceController,
391+ entranceCurve: entranceCurve,
392+ child: Container (
393+ constraints: BoxConstraints (maxWidth: maxWidth, maxHeight: maxHeight),
394+ child: Stack (
395+ children: [
396+ flexDescription,
397+ verticalAxisDescription,
398+ horizontalAxisDescription,
399+ ],
400+ ),
394401 ),
395402 );
396403 }
@@ -405,7 +412,6 @@ class FlexLayoutExplorerWidgetState
405412class VisualizeFlexChildren extends StatefulWidget {
406413 const VisualizeFlexChildren ({
407414 super .key,
408- required this .state,
409415 required this .properties,
410416 required this .children,
411417 required this .highlighted,
@@ -418,7 +424,6 @@ class VisualizeFlexChildren extends StatefulWidget {
418424 final LayoutProperties ? highlighted;
419425 final ScrollController scrollController;
420426 final Axis direction;
421- final FlexLayoutExplorerWidgetState state;
422427
423428 @override
424429 State <VisualizeFlexChildren > createState () => _VisualizeFlexChildrenState ();
@@ -499,7 +504,6 @@ class _VisualizeFlexChildrenState extends State<VisualizeFlexChildren> {
499504
500505 final visualizer = FlexChildVisualizer (
501506 key: isSelected ? selectedChildKey : null ,
502- state: widget.state,
503507 layoutProperties: child,
504508 isSelected: isSelected,
505509 renderProperties: renderProperties[i],
@@ -573,44 +577,39 @@ class _VisualizeFlexChildrenState extends State<VisualizeFlexChildren> {
573577class FlexChildVisualizer extends StatelessWidget {
574578 const FlexChildVisualizer ({
575579 super .key,
576- required this .state,
577580 required this .layoutProperties,
578581 required this .renderProperties,
579582 required this .isSelected,
580583 });
581584
582- final FlexLayoutExplorerWidgetState state;
583-
584585 final bool isSelected;
585586
586587 final LayoutProperties layoutProperties;
587588
588589 final RenderProperties renderProperties;
589590
590- // TODO(polina-c, jacob314): consider refactoring to remove `!`.
591- FlexLayoutProperties get root => state.properties! ;
592-
593591 LayoutProperties get properties => renderProperties.layoutProperties;
594592
595593 ObjectGroup ? get objectGroup =>
596594 properties.node.objectGroupApi as ObjectGroup ? ;
597595
598- void onChangeFlexFactor (int ? newFlexFactor) async {
599- state. markAsDirty ();
596+ void _onChangeFlexFactor (int ? newFlexFactor, VoidCallback markAsDirty ) async {
597+ markAsDirty ();
600598 await objectGroup! .invokeSetFlexFactor (
601599 properties.node.valueRef,
602600 newFlexFactor,
603601 );
604602 }
605603
606- void onChangeFlexFit (FlexFit ? newFlexFit) async {
607- state. markAsDirty ();
604+ void _onChangeFlexFit (FlexFit ? newFlexFit, VoidCallback markAsDirty ) async {
605+ markAsDirty ();
608606 await objectGroup! .invokeSetFlexFit (properties.node.valueRef, newFlexFit! );
609607 }
610608
611609 Widget _buildFlexFactorChangerDropdown (
612610 int maximumFlexFactor,
613611 ThemeData theme,
612+ VoidCallback markAsDirty,
614613 ) {
615614 final propertiesLocal = properties;
616615
@@ -632,7 +631,7 @@ class FlexChildVisualizer extends StatelessWidget {
632631
633632 return DropdownButton <int >(
634633 value: propertiesLocal.flexFactor? .toInt ().clamp (0 , maximumFlexFactor),
635- onChanged: onChangeFlexFactor ,
634+ onChanged: (newFactor) => _onChangeFlexFactor (newFactor, markAsDirty) ,
636635 iconEnabledColor: textColor,
637636 underline: buildUnderline (),
638637 items: < DropdownMenuItem <int >> [
@@ -642,7 +641,10 @@ class FlexChildVisualizer extends StatelessWidget {
642641 );
643642 }
644643
645- Widget _buildFlexFitChangerDropdown (ThemeData theme) {
644+ Widget _buildFlexFitChangerDropdown (
645+ ThemeData theme,
646+ VoidCallback markAsDirty,
647+ ) {
646648 Widget flexFitDescription (FlexFit flexFit) => Text (
647649 'fit: ${flexFit .name }' ,
648650 style: theme.regularTextStyleWithColor (emphasizedTextColor),
@@ -664,7 +666,7 @@ class FlexChildVisualizer extends StatelessWidget {
664666
665667 return DropdownButton <FlexFit >(
666668 value: propertiesLocal.flexFit,
667- onChanged: onChangeFlexFit ,
669+ onChanged: (newFit) => _onChangeFlexFit (newFit, markAsDirty) ,
668670 underline: buildUnderline (),
669671 iconEnabledColor: emphasizedTextColor,
670672 items: < DropdownMenuItem <FlexFit >> [
@@ -675,7 +677,7 @@ class FlexChildVisualizer extends StatelessWidget {
675677 );
676678 }
677679
678- Widget _buildContent (ThemeData theme) {
680+ Widget _buildContent (ThemeData theme, _FlexLayoutExplorerScope scope ) {
679681 // TODO(https://github.com/flutter/devtools/issues/4058) allow more dynamic
680682 // flex factor input
681683 final currentFlexFactor = properties.flexFactor? .toInt () ?? 0 ;
@@ -690,11 +692,15 @@ class FlexChildVisualizer extends StatelessWidget {
690692 crossAxisAlignment: CrossAxisAlignment .end,
691693 children: [
692694 Flexible (
693- child: _buildFlexFactorChangerDropdown (currentMaxFlexFactor, theme),
695+ child: _buildFlexFactorChangerDropdown (
696+ currentMaxFlexFactor,
697+ theme,
698+ scope._markAsDirty,
699+ ),
694700 ),
695701 if (! properties.hasFlexFactor)
696702 Text (
697- 'unconstrained ${root .isMainAxisHorizontal ? 'horizontal' : 'vertical' }' ,
703+ 'unconstrained ${scope . _rootProperties .isMainAxisHorizontal ? 'horizontal' : 'vertical' }' ,
698704 style: theme.regularTextStyle.copyWith (
699705 color: theme.colorScheme.unconstrainedColor,
700706 fontStyle: FontStyle .italic,
@@ -705,18 +711,19 @@ class FlexChildVisualizer extends StatelessWidget {
705711 textScaler: const TextScaler .linear (smallTextScaleFactor),
706712 textAlign: TextAlign .center,
707713 ),
708- _buildFlexFitChangerDropdown (theme),
714+ _buildFlexFitChangerDropdown (theme, scope._markAsDirty ),
709715 ],
710716 ),
711717 );
712718 }
713719
714720 @override
715721 Widget build (BuildContext context) {
722+ final scope = _FlexLayoutExplorerScope ._of (context);
716723 final renderSize = renderProperties.size;
717724 final renderOffset = renderProperties.offset;
718725 final propertiesLocal = properties;
719- final rootLocal = root ;
726+ final rootLocal = scope._rootProperties ;
720727
721728 Widget buildEntranceAnimation (BuildContext _, Widget ? child) {
722729 final vertical = rootLocal.isMainAxisVertical;
@@ -730,11 +737,11 @@ class FlexChildVisualizer extends StatelessWidget {
730737 vertical ? minRenderHeight - entranceMargin : renderSize.height,
731738 ),
732739 end: renderSize,
733- ).evaluate (state.entranceCurve )!
740+ ).evaluate (scope._entranceCurve )!
734741 : renderSize;
735742 // Not-expanded widgets enter much faster.
736743 return Opacity (
737- opacity: min ([state.entranceCurve .value * 5 , 1.0 ]),
744+ opacity: min ([scope._entranceCurve .value * 5 , 1.0 ]),
738745 child: Padding (
739746 padding: EdgeInsets .symmetric (
740747 horizontal: math.max (0.0 , (renderSize.width - size.width) / 2 ),
@@ -749,14 +756,14 @@ class FlexChildVisualizer extends StatelessWidget {
749756 top: renderOffset.dy,
750757 left: renderOffset.dx,
751758 child: GestureDetector (
752- onTap: () => unawaited (state. onTap (propertiesLocal)),
753- onDoubleTap: () => state. onDoubleTap (propertiesLocal),
754- onLongPress: () => state. onDoubleTap (propertiesLocal),
759+ onTap: () => unawaited (scope. _onTap (propertiesLocal)),
760+ onDoubleTap: () => scope. _onDoubleTap (propertiesLocal),
761+ onLongPress: () => scope. _onDoubleTap (propertiesLocal),
755762 child: SizedBox (
756763 width: renderSize.width,
757764 height: renderSize.height,
758765 child: AnimatedBuilder (
759- animation: state.entranceController ,
766+ animation: scope._entranceController ,
760767 builder: buildEntranceAnimation,
761768 child: WidgetVisualizer (
762769 isFlex: true ,
@@ -769,7 +776,7 @@ class FlexChildVisualizer extends StatelessWidget {
769776 properties: propertiesLocal,
770777 child: Align (
771778 alignment: Alignment .topRight,
772- child: _buildContent (Theme .of (context)),
779+ child: _buildContent (Theme .of (context), scope ),
773780 ),
774781 ),
775782 ),
@@ -783,3 +790,58 @@ class FlexChildVisualizer extends StatelessWidget {
783790 /// for example if it's set to 5 the dropdown will consist of 6 items (null and 0..5)
784791 static const maximumFlexFactorOptions = 5 ;
785792}
793+
794+ /// Scope providing [FlexLayoutProperties] and callbacks for
795+ /// [FlexLayoutExplorerWidget] descendants.
796+ class _FlexLayoutExplorerScope extends InheritedWidget {
797+ const _FlexLayoutExplorerScope ({
798+ required FlexLayoutProperties rootProperties,
799+ required Future <void > Function (LayoutProperties properties) onTap,
800+ required void Function (LayoutProperties properties) onDoubleTap,
801+ required VoidCallback markAsDirty,
802+ required AnimationController entranceController,
803+ required CurvedAnimation entranceCurve,
804+ required super .child,
805+ }) : _rootProperties = rootProperties,
806+ _onTap = onTap,
807+ _onDoubleTap = onDoubleTap,
808+ _markAsDirty = markAsDirty,
809+ _entranceController = entranceController,
810+ _entranceCurve = entranceCurve;
811+
812+ /// The properties of the root flex widget being inspected.
813+ final FlexLayoutProperties _rootProperties;
814+
815+ /// Callback when a child widget is tapped.
816+ final Future <void > Function (LayoutProperties properties) _onTap;
817+
818+ /// Callback when a child widget is double tapped.
819+ final void Function (LayoutProperties properties) _onDoubleTap;
820+
821+ /// Callback to mark the layout explorer as dirty for a future refresh.
822+ final VoidCallback _markAsDirty;
823+
824+ /// The animation controller for the entrance animation.
825+ final AnimationController _entranceController;
826+
827+ /// The curved animation for the entrance animation.
828+ final CurvedAnimation _entranceCurve;
829+
830+ /// Retrieves the nearest [_FlexLayoutExplorerScope] ancestor.
831+ static _FlexLayoutExplorerScope _of (BuildContext context) {
832+ final scope = context
833+ .dependOnInheritedWidgetOfExactType <_FlexLayoutExplorerScope >();
834+ assert (scope != null , 'No _FlexLayoutExplorerScope found in context' );
835+ return scope! ;
836+ }
837+
838+ @override
839+ bool updateShouldNotify (_FlexLayoutExplorerScope oldWidget) {
840+ return _rootProperties != oldWidget._rootProperties ||
841+ _onTap != oldWidget._onTap ||
842+ _onDoubleTap != oldWidget._onDoubleTap ||
843+ _markAsDirty != oldWidget._markAsDirty ||
844+ _entranceController != oldWidget._entranceController ||
845+ _entranceCurve != oldWidget._entranceCurve;
846+ }
847+ }
0 commit comments