Skip to content

Commit eb34a12

Browse files
author
Albertus Angga Raharja
committed
Partial highlight constraint and change deserialize method of FlexProperties
1 parent bb1c30f commit eb34a12

11 files changed

+384
-155
lines changed

packages/devtools_app/lib/src/inspector/diagnostics_node.dart

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,22 @@ import 'package:vm_service/vm_service.dart';
1111
import '../ui/fake_flutter/fake_flutter.dart';
1212
import '../ui/icons.dart';
1313
import '../utils.dart';
14+
import 'enum_deserializer.dart';
1415
import 'flutter_widget.dart';
1516
import 'inspector_service.dart';
1617

1718
Map<K, V> _invertMap<K, V>(Map<V, K> inverted) => Map.fromEntries(
1819
inverted.entries.map((entry) => MapEntry(entry.value, entry.key)));
1920

20-
const Map<String, DiagnosticLevel> nameToDiagnosticLevel = {
21-
'hidden': DiagnosticLevel.hidden,
22-
'fine': DiagnosticLevel.fine,
23-
'debug': DiagnosticLevel.debug,
24-
'info': DiagnosticLevel.info,
25-
'warning': DiagnosticLevel.warning,
26-
'hint': DiagnosticLevel.hint,
27-
'summary': DiagnosticLevel.summary,
28-
'error': DiagnosticLevel.error,
29-
'off': DiagnosticLevel.off,
30-
};
21+
final Map<String, DiagnosticLevel> nameToDiagnosticLevel =
22+
EnumDeserializer<DiagnosticLevel>(DiagnosticLevel.values).lookupTable;
3123

3224
final Map<DiagnosticLevel, String> diagnosticLevelToName =
3325
_invertMap(nameToDiagnosticLevel);
3426

35-
const Map<String, DiagnosticsTreeStyle> nameToTreeStyle = {
36-
'sparse': DiagnosticsTreeStyle.sparse,
37-
'offstage': DiagnosticsTreeStyle.offstage,
38-
'dense': DiagnosticsTreeStyle.dense,
39-
'transition': DiagnosticsTreeStyle.transition,
40-
'whitespace': DiagnosticsTreeStyle.whitespace,
41-
'error': DiagnosticsTreeStyle.error,
42-
'flat': DiagnosticsTreeStyle.flat,
43-
'singleLine': DiagnosticsTreeStyle.singleLine,
44-
'errorProperty': DiagnosticsTreeStyle.errorProperty,
45-
'shallow': DiagnosticsTreeStyle.shallow,
46-
'truncateChildren': DiagnosticsTreeStyle.truncateChildren,
47-
};
27+
final Map<String, DiagnosticsTreeStyle> nameToTreeStyle =
28+
EnumDeserializer<DiagnosticsTreeStyle>(DiagnosticsTreeStyle.values)
29+
.lookupTable;
4830

4931
final Map<DiagnosticsTreeStyle, String> treeStyleToName =
5032
_invertMap(nameToTreeStyle);
@@ -97,7 +79,7 @@ class RemoteDiagnosticsNode extends DiagnosticableTree {
9779
// TODO(albertusangga): Confirm all new getter
9880
bool get isFlex => getBooleanMember('isFlex', false);
9981

100-
String get constraints => getStringMember('constraints');
82+
Map<String, Object> get constraints => json['constraints'];
10183

10284
bool get shouldHighlightConstraints =>
10385
getBooleanMember('shouldHighlightConstraints', false);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import '../ui/fake_flutter/fake_flutter.dart';
2+
3+
/// A class for getting an enum object from its string with high performance
4+
/// will return null for invalid string value
5+
///
6+
/// Description:
7+
/// Currently Flutter framework serialize Enum object by calling describeForEnum()
8+
/// This class contains the reverse of that mapping.
9+
///
10+
///
11+
/// Example usage:
12+
/// enum Color {
13+
/// red, green, blue
14+
/// }
15+
/// ```
16+
/// EnumDeserializer<Color> deserializer = EnumDeserializer(Color.values);
17+
/// deserializer.deserialize('red'); -> Color.red
18+
/// ```
19+
class EnumDeserializer<T> {
20+
// currently there's no way to
21+
EnumDeserializer(List<T> enumValues) {
22+
for (var val in enumValues) lookupTable[describeEnum(val)] = val;
23+
}
24+
25+
final Map<String, T> lookupTable = {};
26+
27+
T deserialize(String enumDescription) {
28+
if (lookupTable.containsKey(enumDescription)) {
29+
return lookupTable[enumDescription];
30+
}
31+
return null;
32+
}
33+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter/rendering.dart';
3+
import 'package:flutter/widgets.dart';
4+
5+
import '../enum_deserializer.dart';
6+
7+
const Type boxConstraintsType = BoxConstraints;
8+
9+
Constraints deserializeConstraints(Map<String, Object> json) {
10+
// TODO(albertusangga): Support SliverConstraint
11+
if (json == null || json['type'] != boxConstraintsType.toString())
12+
return null;
13+
return BoxConstraints(
14+
minWidth: json['minWidth'],
15+
maxWidth: json['hasBoundedWidth'] ? json['maxWidth'] : double.infinity,
16+
minHeight: json['minHeight'],
17+
maxHeight: json['hasBoundedHeight'] ? json['maxHeight'] : double.infinity,
18+
);
19+
}
20+
21+
/// TODO(albertusangga): Move this to [RemoteDiagnosticsNode] once dart:html app is removed
22+
@immutable
23+
class RenderFlexProperties {
24+
const RenderFlexProperties({
25+
this.direction,
26+
this.mainAxisAlignment,
27+
this.mainAxisSize,
28+
this.crossAxisAlignment,
29+
this.textDirection,
30+
this.verticalDirection,
31+
this.textBaseline,
32+
});
33+
34+
final Axis direction;
35+
final MainAxisAlignment mainAxisAlignment;
36+
final MainAxisSize mainAxisSize;
37+
final CrossAxisAlignment crossAxisAlignment;
38+
final TextDirection textDirection;
39+
final VerticalDirection verticalDirection;
40+
final TextBaseline textBaseline;
41+
42+
static final directionDeserializer = EnumDeserializer<Axis>(Axis.values);
43+
static final mainAxisAlignmentDeserializer =
44+
EnumDeserializer<MainAxisAlignment>(MainAxisAlignment.values);
45+
static final mainAxisSizeDeserializer =
46+
EnumDeserializer<MainAxisSize>(MainAxisSize.values);
47+
static final crossAxisAlignmentDeserializer =
48+
EnumDeserializer<CrossAxisAlignment>(CrossAxisAlignment.values);
49+
static final textDirectionDeserializer =
50+
EnumDeserializer<TextDirection>(TextDirection.values);
51+
static final verticalDirectionDeserializer =
52+
EnumDeserializer<VerticalDirection>(VerticalDirection.values);
53+
static final textBaselineDeserializer =
54+
EnumDeserializer<TextBaseline>(TextBaseline.values);
55+
56+
static RenderFlexProperties fromJson(Map<String, Object> renderObjectJson) {
57+
final List<dynamic> properties = renderObjectJson['properties'];
58+
// TODO(albertusangga) should we do some checking in the validity of the API contract here?
59+
final Map<String, Object> data = Map.fromEntries(
60+
properties.map(
61+
(property) => MapEntry<String, Object>(
62+
property['name'],
63+
property['description'],
64+
),
65+
),
66+
);
67+
68+
return RenderFlexProperties(
69+
direction: directionDeserializer.deserialize(data['direction']),
70+
mainAxisAlignment:
71+
mainAxisAlignmentDeserializer.deserialize(data['mainAxisAlignment']),
72+
mainAxisSize: mainAxisSizeDeserializer.deserialize(data['mainAxisSize']),
73+
crossAxisAlignment: crossAxisAlignmentDeserializer
74+
.deserialize(data['crossAxisAlignment']),
75+
textDirection:
76+
textDirectionDeserializer.deserialize(data['textDirection']),
77+
verticalDirection:
78+
verticalDirectionDeserializer.deserialize(data['verticalDirection']),
79+
textBaseline: textBaselineDeserializer.deserialize(data['textBaseline']),
80+
);
81+
}
82+
83+
Type get type => direction == Axis.horizontal ? Row : Column;
84+
}

packages/devtools_app/lib/src/inspector/flutter/inspector_screen.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class _InspectorScreenBodyState extends State<InspectorScreenBody>
125125
child: OutlineButton(
126126
onPressed:
127127
summaryTreeController?.toggleDebugLayoutSummaryEnabled,
128-
child: Label(
128+
child: const Label(
129129
FlutterIcons.lightbulb,
130130
'Show Constraints',
131131
minIncludeTextWidth: 1000,

packages/devtools_app/lib/src/inspector/flutter/inspector_screen_details_tab.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import 'package:flutter/widgets.dart';
44

55
import '../../inspector/inspector_text_styles.dart' as inspector_text_styles;
66
import '../diagnostics_node.dart';
7+
import 'data_models.dart';
78
import 'inspector_tree_flutter.dart';
8-
import 'layout_models.dart';
99

1010
class InspectorDetailsTabController extends StatelessWidget {
1111
const InspectorDetailsTabController({
@@ -117,7 +117,7 @@ class _LayoutDetailsTabState extends State<LayoutDetailsTab>
117117
);
118118
return StoryOfYourFlexWidget(
119119
diagnostic: selected,
120-
properties: FlexProperties.fromJson(selected.renderObject),
120+
properties: RenderFlexProperties.fromJson(selected.renderObject),
121121
);
122122
}
123123

@@ -146,7 +146,7 @@ class StoryOfYourFlexWidget extends StatelessWidget {
146146
final RemoteDiagnosticsNode diagnostic;
147147

148148
// Information about Flex elements that has been deserialize
149-
final FlexProperties properties;
149+
final RenderFlexProperties properties;
150150

151151
@override
152152
Widget build(BuildContext context) {

packages/devtools_app/lib/src/inspector/flutter/inspector_tree_flutter.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ class InspectorTreeControllerFlutter extends Object
164164
animationController.reverse();
165165
}
166166

167-
static const bool isExperimentalStoryOfLayoutEnabled = true;
167+
static const bool isExperimentalStoryOfLayoutEnabled = false;
168168
}
169169

170170
abstract class InspectorControllerClient {

packages/devtools_app/lib/src/inspector/flutter/layout_models.dart

Lines changed: 0 additions & 63 deletions
This file was deleted.

packages/devtools_app/lib/src/inspector/flutter/summary_tree_debug_layout.dart

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
77

88
import '../../inspector/inspector_text_styles.dart' as inspector_text_styles;
99
import '../diagnostics_node.dart';
10-
import '../inspector_controller.dart';
10+
import 'data_models.dart';
1111

1212
class ConstraintsDescription extends AnimatedWidget {
1313
const ConstraintsDescription({
@@ -18,26 +18,70 @@ class ConstraintsDescription extends AnimatedWidget {
1818

1919
final RemoteDiagnosticsNode diagnostic;
2020

21+
String describeDimension(double min, double max, String dim) {
22+
if (min == max) return '$dim=${min.toStringAsFixed(1)}';
23+
return '${min.toStringAsFixed(1)}<=$dim<=${max.toStringAsFixed(1)}';
24+
}
25+
2126
@override
2227
Widget build(BuildContext context) {
2328
if (diagnostic?.constraints == null) {
2429
return const SizedBox();
2530
}
26-
var textStyle = inspector_text_styles.unimportantItalic;
27-
if (diagnostic?.shouldHighlightConstraints ?? false) {
28-
textStyle = textStyle.merge(textStyleForLevel(DiagnosticLevel.warning));
31+
final constraints = deserializeConstraints(diagnostic.constraints);
32+
if (constraints is BoxConstraints) {
33+
final textSpans = <TextSpan>[const TextSpan(text: 'BoxConstraints(')];
34+
if (!constraints.hasBoundedHeight && !constraints.hasBoundedWidth) {
35+
textSpans.add(TextSpan(
36+
text: 'unconstrained', style: inspector_text_styles.warning));
37+
} else {
38+
textSpans.add(
39+
!constraints.hasBoundedWidth
40+
? TextSpan(
41+
text: 'width unconstrained',
42+
style: inspector_text_styles.warning,
43+
)
44+
: TextSpan(
45+
text: describeDimension(
46+
constraints.minWidth,
47+
constraints.maxWidth,
48+
'w',
49+
),
50+
),
51+
);
52+
textSpans.add(const TextSpan(text: ','));
53+
textSpans.add(
54+
!constraints.hasBoundedHeight
55+
? TextSpan(
56+
text: 'height unconstrained',
57+
style: inspector_text_styles.warning,
58+
)
59+
: TextSpan(
60+
text: describeDimension(
61+
constraints.minHeight,
62+
constraints.maxHeight,
63+
'h',
64+
),
65+
),
66+
);
67+
}
68+
textSpans.add(const TextSpan(text: ')'));
69+
final child = Container(
70+
padding: const EdgeInsets.symmetric(horizontal: 4.0),
71+
child: RichText(
72+
text: TextSpan(
73+
style: inspector_text_styles.unimportantItalic,
74+
children: textSpans,
75+
),
76+
),
77+
);
78+
return FadeTransition(
79+
opacity: listenable,
80+
child: child,
81+
);
82+
} else {
83+
// TODO(albertusangga) Support SliverConstraint
84+
return const SizedBox();
2985
}
30-
final child = Container(
31-
padding: const EdgeInsets.symmetric(horizontal: 4.0),
32-
child: Text(
33-
'${diagnostic.constraints}',
34-
style: textStyle,
35-
),
36-
);
37-
if (listenable == null) return child;
38-
return FadeTransition(
39-
opacity: listenable,
40-
child: child,
41-
);
4286
}
4387
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import 'package:devtools_app/src/inspector/enum_deserializer.dart';
2+
import 'package:flutter_test/flutter_test.dart';
3+
4+
enum Color { red, green, blue }
5+
6+
void main() {
7+
final deserializer = EnumDeserializer<Color>(Color.values);
8+
9+
test('deserialize return null', () {
10+
expect(deserializer.deserialize('red'), Color.red);
11+
expect(deserializer.deserialize('green'), Color.green);
12+
expect(deserializer.deserialize('blue'), Color.blue);
13+
});
14+
15+
test('deserialize return correct enum', () {
16+
expect(deserializer.deserialize('yellow'), null);
17+
});
18+
}

0 commit comments

Comments
 (0)