5
5
import 'dart:html' as html;
6
6
import 'dart:js_util' as js_util;
7
7
import 'dart:math' ;
8
- import 'dart:ui' ;
8
+ import 'dart:ui' as ui ;
9
9
10
+ import 'package:flutter/material.dart' ;
10
11
import 'package:meta/meta.dart' ;
11
12
12
13
import 'recorder.dart' ;
@@ -29,18 +30,18 @@ class ParagraphGenerator {
29
30
30
31
/// Randomizes the given [text] and creates a paragraph with a unique
31
32
/// font-size so that the engine doesn't reuse a cached ruler.
32
- Paragraph generate (
33
+ ui. Paragraph generate (
33
34
String text, {
34
35
int maxLines,
35
36
bool hasEllipsis = false ,
36
37
}) {
37
- final ParagraphBuilder builder = ParagraphBuilder (ParagraphStyle (
38
+ final ui. ParagraphBuilder builder = ui. ParagraphBuilder (ui. ParagraphStyle (
38
39
fontFamily: 'sans-serif' ,
39
40
maxLines: maxLines,
40
41
ellipsis: hasEllipsis ? '...' : null ,
41
42
))
42
43
// Start from a font-size of 8.0 and go up by 0.01 each time.
43
- ..pushStyle (TextStyle (fontSize: 8.0 + _counter * 0.01 ))
44
+ ..pushStyle (ui. TextStyle (fontSize: 8.0 + _counter * 0.01 ))
44
45
..addText (_randomize (text));
45
46
_counter++ ;
46
47
return builder.build ();
@@ -57,6 +58,11 @@ void _useCanvasText(bool useCanvasText) {
57
58
);
58
59
}
59
60
61
+ typedef OnBenchmark = void Function (String name, num value);
62
+ void _onBenchmark (OnBenchmark listener) {
63
+ js_util.setProperty (html.window, '_flutter_internal_on_benchmark' , listener);
64
+ }
65
+
60
66
/// Repeatedly lays out a paragraph using the DOM measurement approach.
61
67
///
62
68
/// Creates a different paragraph each time in order to avoid hitting the cache.
@@ -119,13 +125,13 @@ class BenchTextLayout extends RawRecorder {
119
125
120
126
void recordParagraphOperations ({
121
127
@required Profile profile,
122
- @required Paragraph paragraph,
128
+ @required ui. Paragraph paragraph,
123
129
@required String text,
124
130
@required String keyPrefix,
125
131
@required double maxWidth,
126
132
}) {
127
133
profile.record ('$keyPrefix .layout' , () {
128
- paragraph.layout (ParagraphConstraints (width: maxWidth));
134
+ paragraph.layout (ui. ParagraphConstraints (width: maxWidth));
129
135
});
130
136
profile.record ('$keyPrefix .getBoxesForRange' , () {
131
137
for (int start = 0 ; start < text.length; start += 3 ) {
@@ -159,9 +165,9 @@ class BenchTextCachedLayout extends RawRecorder {
159
165
/// Whether to use the new canvas-based text measurement implementation.
160
166
final bool useCanvas;
161
167
162
- final ParagraphBuilder builder =
163
- ParagraphBuilder (ParagraphStyle (fontFamily: 'sans-serif' ))
164
- ..pushStyle (TextStyle (fontSize: 12.0 ))
168
+ final ui. ParagraphBuilder builder =
169
+ ui. ParagraphBuilder (ui. ParagraphStyle (fontFamily: 'sans-serif' ))
170
+ ..pushStyle (ui. TextStyle (fontSize: 12.0 ))
165
171
..addText (
166
172
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, '
167
173
'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' ,
@@ -170,10 +176,292 @@ class BenchTextCachedLayout extends RawRecorder {
170
176
@override
171
177
void body (Profile profile) {
172
178
_useCanvasText (useCanvas);
173
- final Paragraph paragraph = builder.build ();
179
+ final ui. Paragraph paragraph = builder.build ();
174
180
profile.record ('layout' , () {
175
- paragraph.layout (const ParagraphConstraints (width: double .infinity));
181
+ paragraph.layout (const ui.ParagraphConstraints (width: double .infinity));
182
+ });
183
+ _useCanvasText (null );
184
+ }
185
+ }
186
+
187
+ /// Global counter incremented every time the benchmark is asked to
188
+ /// [createWidget] .
189
+ ///
190
+ /// The purpose of this counter is to make sure the rendered paragraphs on each
191
+ /// build are unique.
192
+ int _counter = 0 ;
193
+
194
+ /// Measures how expensive it is to construct material checkboxes.
195
+ ///
196
+ /// Creates a 10x10 grid of tristate checkboxes.
197
+ class BenchBuildColorsGrid extends WidgetBuildRecorder {
198
+ BenchBuildColorsGrid ({@required this .useCanvas})
199
+ : super (name: useCanvas ? canvasBenchmarkName : domBenchmarkName);
200
+
201
+ static const String domBenchmarkName = 'text_dom_color_grid' ;
202
+ static const String canvasBenchmarkName = 'text_canvas_color_grid' ;
203
+
204
+ /// Whether to use the new canvas-based text measurement implementation.
205
+ final bool useCanvas;
206
+
207
+ num _textLayoutMicros = 0 ;
208
+
209
+ @override
210
+ void setUpAll () {
211
+ _useCanvasText (useCanvas);
212
+ _onBenchmark ((String name, num value) {
213
+ _textLayoutMicros += value;
176
214
});
215
+ }
216
+
217
+ @override
218
+ void tearDownAll () {
177
219
_useCanvasText (null );
220
+ _onBenchmark (null );
221
+ }
222
+
223
+ @override
224
+ void frameWillDraw () {
225
+ super .frameWillDraw ();
226
+ _textLayoutMicros = 0 ;
227
+ }
228
+
229
+ @override
230
+ void frameDidDraw () {
231
+ // We need to do this before calling [super.frameDidDraw] because the latter
232
+ // updates the value of [showWidget] in preparation for the next frame.
233
+ if (showWidget) {
234
+ profile.addDataPoint (
235
+ 'text_layout' ,
236
+ Duration (microseconds: _textLayoutMicros.toInt ()),
237
+ );
238
+ }
239
+ super .frameDidDraw ();
240
+ }
241
+
242
+ @override
243
+ Widget createWidget () {
244
+ _counter++ ;
245
+ return MaterialApp (home: ColorsDemo ());
246
+ }
247
+ }
248
+
249
+ // The code below was copied from `colors_demo.dart` in the `flutter_gallery`
250
+ // example.
251
+
252
+ const double kColorItemHeight = 48.0 ;
253
+
254
+ class Palette {
255
+ Palette ({this .name, this .primary, this .accent, this .threshold = 900 });
256
+
257
+ final String name;
258
+ final MaterialColor primary;
259
+ final MaterialAccentColor accent;
260
+ final int
261
+ threshold; // titles for indices > threshold are white, otherwise black
262
+
263
+ bool get isValid => name != null && primary != null && threshold != null ;
264
+ }
265
+
266
+ final List <Palette > allPalettes = < Palette > [
267
+ Palette (
268
+ name: 'RED' ,
269
+ primary: Colors .red,
270
+ accent: Colors .redAccent,
271
+ threshold: 300 ),
272
+ Palette (
273
+ name: 'PINK' ,
274
+ primary: Colors .pink,
275
+ accent: Colors .pinkAccent,
276
+ threshold: 200 ),
277
+ Palette (
278
+ name: 'PURPLE' ,
279
+ primary: Colors .purple,
280
+ accent: Colors .purpleAccent,
281
+ threshold: 200 ),
282
+ Palette (
283
+ name: 'DEEP PURPLE' ,
284
+ primary: Colors .deepPurple,
285
+ accent: Colors .deepPurpleAccent,
286
+ threshold: 200 ),
287
+ Palette (
288
+ name: 'INDIGO' ,
289
+ primary: Colors .indigo,
290
+ accent: Colors .indigoAccent,
291
+ threshold: 200 ),
292
+ Palette (
293
+ name: 'BLUE' ,
294
+ primary: Colors .blue,
295
+ accent: Colors .blueAccent,
296
+ threshold: 400 ),
297
+ Palette (
298
+ name: 'LIGHT BLUE' ,
299
+ primary: Colors .lightBlue,
300
+ accent: Colors .lightBlueAccent,
301
+ threshold: 500 ),
302
+ Palette (
303
+ name: 'CYAN' ,
304
+ primary: Colors .cyan,
305
+ accent: Colors .cyanAccent,
306
+ threshold: 600 ),
307
+ Palette (
308
+ name: 'TEAL' ,
309
+ primary: Colors .teal,
310
+ accent: Colors .tealAccent,
311
+ threshold: 400 ),
312
+ Palette (
313
+ name: 'GREEN' ,
314
+ primary: Colors .green,
315
+ accent: Colors .greenAccent,
316
+ threshold: 500 ),
317
+ Palette (
318
+ name: 'LIGHT GREEN' ,
319
+ primary: Colors .lightGreen,
320
+ accent: Colors .lightGreenAccent,
321
+ threshold: 600 ),
322
+ Palette (
323
+ name: 'LIME' ,
324
+ primary: Colors .lime,
325
+ accent: Colors .limeAccent,
326
+ threshold: 800 ),
327
+ Palette (name: 'YELLOW' , primary: Colors .yellow, accent: Colors .yellowAccent),
328
+ Palette (name: 'AMBER' , primary: Colors .amber, accent: Colors .amberAccent),
329
+ Palette (
330
+ name: 'ORANGE' ,
331
+ primary: Colors .orange,
332
+ accent: Colors .orangeAccent,
333
+ threshold: 700 ),
334
+ Palette (
335
+ name: 'DEEP ORANGE' ,
336
+ primary: Colors .deepOrange,
337
+ accent: Colors .deepOrangeAccent,
338
+ threshold: 400 ),
339
+ Palette (name: 'BROWN' , primary: Colors .brown, threshold: 200 ),
340
+ Palette (name: 'GREY' , primary: Colors .grey, threshold: 500 ),
341
+ Palette (name: 'BLUE GREY' , primary: Colors .blueGrey, threshold: 500 ),
342
+ ];
343
+
344
+ class ColorItem extends StatelessWidget {
345
+ const ColorItem ({
346
+ Key key,
347
+ @required this .index,
348
+ @required this .color,
349
+ this .prefix = '' ,
350
+ }) : assert (index != null ),
351
+ assert (color != null ),
352
+ assert (prefix != null ),
353
+ super (key: key);
354
+
355
+ final int index;
356
+ final Color color;
357
+ final String prefix;
358
+
359
+ String colorString () =>
360
+ "$_counter :#${color .value .toRadixString (16 ).padLeft (8 , '0' ).toUpperCase ()}" ;
361
+
362
+ @override
363
+ Widget build (BuildContext context) {
364
+ return Semantics (
365
+ container: true ,
366
+ child: Container (
367
+ height: kColorItemHeight,
368
+ padding: const EdgeInsets .symmetric (horizontal: 16.0 ),
369
+ color: color,
370
+ child: SafeArea (
371
+ top: false ,
372
+ bottom: false ,
373
+ child: Row (
374
+ mainAxisAlignment: MainAxisAlignment .spaceBetween,
375
+ crossAxisAlignment: CrossAxisAlignment .center,
376
+ children: < Widget > [
377
+ Text ('$_counter :$prefix $index ' ),
378
+ Text (colorString ()),
379
+ ],
380
+ ),
381
+ ),
382
+ ),
383
+ );
384
+ }
385
+ }
386
+
387
+ class PaletteTabView extends StatelessWidget {
388
+ PaletteTabView ({
389
+ Key key,
390
+ @required this .colors,
391
+ }) : assert (colors != null && colors.isValid),
392
+ super (key: key);
393
+
394
+ final Palette colors;
395
+
396
+ static const List <int > primaryKeys = < int > [
397
+ 50 ,
398
+ 100 ,
399
+ 200 ,
400
+ 300 ,
401
+ 400 ,
402
+ 500 ,
403
+ 600 ,
404
+ 700 ,
405
+ 800 ,
406
+ 900
407
+ ];
408
+ static const List <int > accentKeys = < int > [100 , 200 , 400 , 700 ];
409
+
410
+ @override
411
+ Widget build (BuildContext context) {
412
+ final TextTheme textTheme = Theme .of (context).textTheme;
413
+ final TextStyle whiteTextStyle =
414
+ textTheme.bodyText2.copyWith (color: Colors .white);
415
+ final TextStyle blackTextStyle =
416
+ textTheme.bodyText2.copyWith (color: Colors .black);
417
+ return Scrollbar (
418
+ child: ListView (
419
+ itemExtent: kColorItemHeight,
420
+ children: < Widget > [
421
+ ...primaryKeys.map <Widget >((int index) {
422
+ return DefaultTextStyle (
423
+ style: index > colors.threshold ? whiteTextStyle : blackTextStyle,
424
+ child: ColorItem (index: index, color: colors.primary[index]),
425
+ );
426
+ }),
427
+ if (colors.accent != null )
428
+ ...accentKeys.map <Widget >((int index) {
429
+ return DefaultTextStyle (
430
+ style:
431
+ index > colors.threshold ? whiteTextStyle : blackTextStyle,
432
+ child: ColorItem (
433
+ index: index, color: colors.accent[index], prefix: 'A' ),
434
+ );
435
+ }),
436
+ ],
437
+ ),
438
+ );
439
+ }
440
+ }
441
+
442
+ class ColorsDemo extends StatelessWidget {
443
+ @override
444
+ Widget build (BuildContext context) {
445
+ return DefaultTabController (
446
+ length: allPalettes.length,
447
+ child: Scaffold (
448
+ appBar: AppBar (
449
+ elevation: 0.0 ,
450
+ title: const Text ('Colors' ),
451
+ bottom: TabBar (
452
+ isScrollable: true ,
453
+ tabs: allPalettes
454
+ .map <Widget >(
455
+ (Palette swatch) => Tab (text: '$_counter :${swatch .name }' ))
456
+ .toList (),
457
+ ),
458
+ ),
459
+ body: TabBarView (
460
+ children: allPalettes.map <Widget >((Palette colors) {
461
+ return PaletteTabView (colors: colors);
462
+ }).toList (),
463
+ ),
464
+ ),
465
+ );
178
466
}
179
467
}
0 commit comments