@@ -40,50 +40,6 @@ class _FNestedHeader extends FHeader {
40
40
final style = this .style? .call (context.theme.headerStyles.nestedStyle) ?? context.theme.headerStyles.nestedStyle;
41
41
final alignment = titleAlignment.resolve (Directionality .maybeOf (context) ?? TextDirection .ltr);
42
42
43
- Widget title = Align (
44
- alignment: alignment,
45
- child: Padding (
46
- padding: const EdgeInsets .symmetric (horizontal: 10.0 ),
47
- child: DefaultTextStyle .merge (
48
- overflow: TextOverflow .fade,
49
- maxLines: 1 ,
50
- softWrap: false ,
51
- style: style.titleTextStyle,
52
- child: this .title,
53
- ),
54
- ),
55
- );
56
-
57
- if (prefixes.isNotEmpty || suffixes.isNotEmpty) {
58
- final spacing = SizedBox (width: style.actionSpacing);
59
- final prefixes = Row (
60
- mainAxisSize: MainAxisSize .min,
61
- children: separate (this .prefixes, by: [spacing]),
62
- );
63
- final suffixes = Row (
64
- mainAxisSize: MainAxisSize .min,
65
- children: separate (this .suffixes, by: [spacing]),
66
- );
67
-
68
- // We use a stack as a row could result in the title being off centered if the icon on the left or right is
69
- // missing/different sizes.
70
- title = alignment.x == 0
71
- ? Stack (
72
- children: [
73
- title,
74
- Align (alignment: Alignment .centerLeft, child: prefixes),
75
- Align (alignment: Alignment .centerRight, child: suffixes),
76
- ],
77
- )
78
- : Row (
79
- children: [
80
- prefixes,
81
- Expanded (child: title),
82
- suffixes,
83
- ],
84
- );
85
- }
86
-
87
43
Widget header = SafeArea (
88
44
bottom: false ,
89
45
child: Semantics (
@@ -92,7 +48,28 @@ class _FNestedHeader extends FHeader {
92
48
decoration: style.decoration,
93
49
child: Padding (
94
50
padding: style.padding,
95
- child: FHeaderData (actionStyle: style.actionStyle, child: title),
51
+ child: FHeaderData (
52
+ actionStyle: style.actionStyle,
53
+ child: _NestedHeader (
54
+ alignment: alignment,
55
+ prefixes: Row (mainAxisSize: MainAxisSize .min, spacing: style.actionSpacing, children: prefixes),
56
+ title: Padding (
57
+ padding: const EdgeInsets .symmetric (horizontal: 10.0 ),
58
+ child: DefaultTextStyle .merge (
59
+ overflow: TextOverflow .ellipsis,
60
+ maxLines: 1 ,
61
+ softWrap: false ,
62
+ style: style.titleTextStyle,
63
+ textHeightBehavior: const TextHeightBehavior (
64
+ applyHeightToFirstAscent: false ,
65
+ applyHeightToLastDescent: false ,
66
+ ),
67
+ child: title,
68
+ ),
69
+ ),
70
+ suffixes: Row (mainAxisSize: MainAxisSize .min, spacing: style.actionSpacing, children: suffixes),
71
+ ),
72
+ ),
96
73
),
97
74
),
98
75
),
@@ -122,3 +99,106 @@ class _FNestedHeader extends FHeader {
122
99
..add (DiagnosticsProperty ('titleAlignment' , titleAlignment));
123
100
}
124
101
}
102
+
103
+ class _NestedHeader extends MultiChildRenderObjectWidget {
104
+ final Alignment alignment;
105
+
106
+ _NestedHeader ({required this .alignment, required Widget prefixes, required Widget title, required Widget suffixes})
107
+ : super (children: [prefixes, title, suffixes]);
108
+
109
+ @override
110
+ RenderObject createRenderObject (BuildContext context) =>
111
+ _RenderNestedHeader (alignment: alignment, textDirection: Directionality .of (context));
112
+
113
+ @override
114
+ void updateRenderObject (BuildContext context, covariant _RenderNestedHeader renderObject) => renderObject
115
+ ..alignment = alignment
116
+ ..direction = Directionality .of (context);
117
+
118
+ @override
119
+ void debugFillProperties (DiagnosticPropertiesBuilder properties) {
120
+ super .debugFillProperties (properties);
121
+ properties.add (DiagnosticsProperty ('alignment' , alignment));
122
+ }
123
+ }
124
+
125
+ class _RenderNestedHeader extends RenderBox
126
+ with ContainerRenderObjectMixin <RenderBox , DefaultData >, RenderBoxContainerDefaultsMixin <RenderBox , DefaultData > {
127
+ Alignment _alignment;
128
+ TextDirection _direction;
129
+
130
+ _RenderNestedHeader ({required Alignment alignment, required TextDirection textDirection})
131
+ : _alignment = alignment,
132
+ _direction = textDirection;
133
+
134
+ @override
135
+ void setupParentData (RenderBox child) => child.parentData = DefaultData ();
136
+
137
+ @override
138
+ void performLayout () {
139
+ if (childCount == 0 ) {
140
+ size = constraints.smallest;
141
+ return ;
142
+ }
143
+
144
+ final prefixes = firstChild! ;
145
+ final title = childAfter (prefixes)! ;
146
+ final suffixes = childAfter (title)! ;
147
+
148
+ // We prioritize the prefixes and suffixes since they are interactive.
149
+ prefixes.layout (constraints, parentUsesSize: true );
150
+ suffixes.layout (constraints, parentUsesSize: true );
151
+ title.layout (
152
+ constraints.copyWith (maxWidth: constraints.maxWidth - prefixes.size.width - suffixes.size.width),
153
+ parentUsesSize: true ,
154
+ );
155
+
156
+ final height = [title.size.height, prefixes.size.height, suffixes.size.height].reduce (max);
157
+ size = constraints.constrain (Size (constraints.maxWidth, height));
158
+
159
+ final (left, right) = _direction == TextDirection .ltr ? (prefixes, suffixes) : (suffixes, prefixes);
160
+ left.data.offset = Offset (0 , (size.height - left.size.height) / 2 );
161
+ right.data.offset = Offset (size.width - right.size.width, (size.height - right.size.height) / 2 );
162
+
163
+ // Position title based on Alignment (-1 to 1) on each axis where 0 is center.
164
+ // Title x-axis is relative to the center of the NestedHeader container
165
+ final titleX = (size.width - title.size.width) / 2 * (alignment.x + 1 );
166
+ final titleY = (size.height - title.size.height) * (alignment.y + 1 ) / 2 ;
167
+ title.data.offset = Offset (titleX.clamp (left.size.width, size.width - right.size.width - title.size.width), titleY);
168
+ }
169
+
170
+ @override
171
+ void paint (PaintingContext context, Offset offset) => defaultPaint (context, offset);
172
+
173
+ @override
174
+ bool hitTestChildren (BoxHitTestResult result, {required Offset position}) =>
175
+ defaultHitTestChildren (result, position: position);
176
+
177
+ Alignment get alignment => _alignment;
178
+
179
+ set alignment (Alignment value) {
180
+ if (_alignment == value) {
181
+ return ;
182
+ }
183
+ _alignment = value;
184
+ markNeedsLayout ();
185
+ }
186
+
187
+ TextDirection get direction => _direction;
188
+
189
+ set direction (TextDirection value) {
190
+ if (_direction == value) {
191
+ return ;
192
+ }
193
+ _direction = value;
194
+ markNeedsLayout ();
195
+ }
196
+
197
+ @override
198
+ void debugFillProperties (DiagnosticPropertiesBuilder properties) {
199
+ super .debugFillProperties (properties);
200
+ properties
201
+ ..add (DiagnosticsProperty ('alignment' , alignment))
202
+ ..add (EnumProperty ('direction' , direction));
203
+ }
204
+ }
0 commit comments