@@ -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,54 @@ 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+ super .key,
799+ required this .rootProperties,
800+ required this .onTap,
801+ required this .onDoubleTap,
802+ required this .markAsDirty,
803+ required this .entranceController,
804+ required this .entranceCurve,
805+ required super .child,
806+ });
807+
808+ /// The properties of the root flex widget being inspected.
809+ final FlexLayoutProperties rootProperties;
810+
811+ /// Callback when a child widget is tapped.
812+ final Future <void > Function (LayoutProperties properties) onTap;
813+
814+ /// Callback when a child widget is double tapped.
815+ final void Function (LayoutProperties properties) onDoubleTap;
816+
817+ /// Callback to mark the layout explorer as dirty for a future refresh.
818+ final VoidCallback markAsDirty;
819+
820+ /// The animation controller for the entrance animation.
821+ final AnimationController entranceController;
822+
823+ /// The curved animation for the entrance animation.
824+ final CurvedAnimation entranceCurve;
825+
826+ /// Retrieves the nearest [FlexLayoutExplorerScope] ancestor.
827+ static FlexLayoutExplorerScope of (BuildContext context) {
828+ final scope = context
829+ .dependOnInheritedWidgetOfExactType <FlexLayoutExplorerScope >();
830+ assert (scope != null , 'No FlexLayoutExplorerScope found in context' );
831+ return scope! ;
832+ }
833+
834+ @override
835+ bool updateShouldNotify (FlexLayoutExplorerScope oldWidget) {
836+ return rootProperties != oldWidget.rootProperties ||
837+ onTap != oldWidget.onTap ||
838+ onDoubleTap != oldWidget.onDoubleTap ||
839+ markAsDirty != oldWidget.markAsDirty ||
840+ entranceController != oldWidget.entranceController ||
841+ entranceCurve != oldWidget.entranceCurve;
842+ }
843+ }
0 commit comments