TextPainter RTL (#11888)
This commit is contained in:
parent
f4ccb4b6a4
commit
ca7d2d23cf
@ -1 +1 @@
|
|||||||
ccf68cdcb66f9fe354bf3e8472bb0b47c83e8ac9
|
2d7c30033d76fda9779462827ad5322d78e1fb3a
|
||||||
|
7
examples/hello_world/README.md
Normal file
7
examples/hello_world/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
```
|
||||||
|
# To run the Hello World demo:
|
||||||
|
flutter run
|
||||||
|
|
||||||
|
# To run the Hello World demo showing Arabic:
|
||||||
|
flutter run lib/arabic.dart
|
||||||
|
```
|
7
examples/hello_world/lib/arabic.dart
Normal file
7
examples/hello_world/lib/arabic.dart
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
void main() => runApp(const Center(child: const Text('برنامج أهلا بالعالم', textDirection: TextDirection.rtl)));
|
@ -4,4 +4,4 @@
|
|||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
void main() => runApp(const Center(child: const Text('Hello, world!')));
|
void main() => runApp(const Center(child: const Text('Hello, world!', textDirection: TextDirection.ltr)));
|
||||||
|
@ -4,4 +4,4 @@
|
|||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
void main() => runApp(const Center(child: const Text('flutter run -t xxx/yyy.dart')));
|
void main() => runApp(const Center(child: const Text('flutter run -t xxx/yyy.dart', textDirection: TextDirection.ltr)));
|
||||||
|
@ -11,7 +11,9 @@ void beginFrame(Duration timeStamp) {
|
|||||||
final double devicePixelRatio = ui.window.devicePixelRatio;
|
final double devicePixelRatio = ui.window.devicePixelRatio;
|
||||||
final ui.Size logicalSize = ui.window.physicalSize / devicePixelRatio;
|
final ui.Size logicalSize = ui.window.physicalSize / devicePixelRatio;
|
||||||
|
|
||||||
final ui.ParagraphBuilder paragraphBuilder = new ui.ParagraphBuilder(new ui.ParagraphStyle())
|
final ui.ParagraphBuilder paragraphBuilder = new ui.ParagraphBuilder(
|
||||||
|
new ui.ParagraphStyle(textDirection: ui.TextDirection.ltr),
|
||||||
|
)
|
||||||
..addText('Hello, world.');
|
..addText('Hello, world.');
|
||||||
final ui.Paragraph paragraph = paragraphBuilder.build()
|
final ui.Paragraph paragraph = paragraphBuilder.build()
|
||||||
..layout(new ui.ParagraphConstraints(width: logicalSize.width));
|
..layout(new ui.ParagraphConstraints(width: logicalSize.width));
|
||||||
|
@ -52,7 +52,13 @@ void beginFrame(Duration timeStamp) {
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// To create a paragraph of text, we use ParagraphBuilder.
|
// To create a paragraph of text, we use ParagraphBuilder.
|
||||||
final ui.ParagraphBuilder builder = new ui.ParagraphBuilder(new ui.ParagraphStyle())
|
final ui.ParagraphBuilder builder = new ui.ParagraphBuilder(
|
||||||
|
// The text below has a primary direction of left-to-right.
|
||||||
|
// The embedded text has other directions.
|
||||||
|
// If this was TextDirection.rtl, the "Hello, world" text would end up on
|
||||||
|
// the other side of the right-to-left text.
|
||||||
|
new ui.ParagraphStyle(textDirection: ui.TextDirection.ltr),
|
||||||
|
)
|
||||||
// We first push a style that turns the text blue.
|
// We first push a style that turns the text blue.
|
||||||
..pushStyle(new ui.TextStyle(color: const ui.Color(0xFF0000FF)))
|
..pushStyle(new ui.TextStyle(color: const ui.Color(0xFF0000FF)))
|
||||||
..addText('Hello, ')
|
..addText('Hello, ')
|
||||||
|
@ -14,24 +14,36 @@ void main() {
|
|||||||
|
|
||||||
void addAlignmentRow(CrossAxisAlignment crossAxisAlignment) {
|
void addAlignmentRow(CrossAxisAlignment crossAxisAlignment) {
|
||||||
TextStyle style = const TextStyle(color: const Color(0xFF000000));
|
TextStyle style = const TextStyle(color: const Color(0xFF000000));
|
||||||
final RenderParagraph paragraph = new RenderParagraph(new TextSpan(style: style, text: '$crossAxisAlignment'));
|
final RenderParagraph paragraph = new RenderParagraph(
|
||||||
|
new TextSpan(style: style, text: '$crossAxisAlignment'),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
);
|
||||||
table.add(new RenderPadding(child: paragraph, padding: const EdgeInsets.only(top: 20.0)));
|
table.add(new RenderPadding(child: paragraph, padding: const EdgeInsets.only(top: 20.0)));
|
||||||
final RenderFlex row = new RenderFlex(crossAxisAlignment: crossAxisAlignment, textBaseline: TextBaseline.alphabetic);
|
final RenderFlex row = new RenderFlex(crossAxisAlignment: crossAxisAlignment, textBaseline: TextBaseline.alphabetic);
|
||||||
style = const TextStyle(fontSize: 15.0, color: const Color(0xFF000000));
|
style = const TextStyle(fontSize: 15.0, color: const Color(0xFF000000));
|
||||||
row.add(new RenderDecoratedBox(
|
row.add(new RenderDecoratedBox(
|
||||||
decoration: const BoxDecoration(color: const Color(0x7FFFCCCC)),
|
decoration: const BoxDecoration(color: const Color(0x7FFFCCCC)),
|
||||||
child: new RenderParagraph(new TextSpan(style: style, text: 'foo foo foo'))
|
child: new RenderParagraph(
|
||||||
|
new TextSpan(style: style, text: 'foo foo foo'),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
),
|
||||||
));
|
));
|
||||||
style = const TextStyle(fontSize: 10.0, color: const Color(0xFF000000));
|
style = const TextStyle(fontSize: 10.0, color: const Color(0xFF000000));
|
||||||
row.add(new RenderDecoratedBox(
|
row.add(new RenderDecoratedBox(
|
||||||
decoration: const BoxDecoration(color: const Color(0x7FCCFFCC)),
|
decoration: const BoxDecoration(color: const Color(0x7FCCFFCC)),
|
||||||
child: new RenderParagraph(new TextSpan(style: style, text: 'foo foo foo'))
|
child: new RenderParagraph(
|
||||||
|
new TextSpan(style: style, text: 'foo foo foo'),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
),
|
||||||
));
|
));
|
||||||
final RenderFlex subrow = new RenderFlex(crossAxisAlignment: crossAxisAlignment, textBaseline: TextBaseline.alphabetic);
|
final RenderFlex subrow = new RenderFlex(crossAxisAlignment: crossAxisAlignment, textBaseline: TextBaseline.alphabetic);
|
||||||
style = const TextStyle(fontSize: 25.0, color: const Color(0xFF000000));
|
style = const TextStyle(fontSize: 25.0, color: const Color(0xFF000000));
|
||||||
subrow.add(new RenderDecoratedBox(
|
subrow.add(new RenderDecoratedBox(
|
||||||
decoration: const BoxDecoration(color: const Color(0x7FCCCCFF)),
|
decoration: const BoxDecoration(color: const Color(0x7FCCCCFF)),
|
||||||
child: new RenderParagraph(new TextSpan(style: style, text: 'foo foo foo foo'))
|
child: new RenderParagraph(
|
||||||
|
new TextSpan(style: style, text: 'foo foo foo foo'),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
),
|
||||||
));
|
));
|
||||||
subrow.add(new RenderSolidColorBox(const Color(0x7FCCFFFF), desiredSize: const Size(30.0, 40.0)));
|
subrow.add(new RenderSolidColorBox(const Color(0x7FCCFFFF), desiredSize: const Size(30.0, 40.0)));
|
||||||
row.add(subrow);
|
row.add(subrow);
|
||||||
@ -48,7 +60,10 @@ void main() {
|
|||||||
|
|
||||||
void addJustificationRow(MainAxisAlignment justify) {
|
void addJustificationRow(MainAxisAlignment justify) {
|
||||||
const TextStyle style = const TextStyle(color: const Color(0xFF000000));
|
const TextStyle style = const TextStyle(color: const Color(0xFF000000));
|
||||||
final RenderParagraph paragraph = new RenderParagraph(new TextSpan(style: style, text: '$justify'));
|
final RenderParagraph paragraph = new RenderParagraph(
|
||||||
|
new TextSpan(style: style, text: '$justify'),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
);
|
||||||
table.add(new RenderPadding(child: paragraph, padding: const EdgeInsets.only(top: 20.0)));
|
table.add(new RenderPadding(child: paragraph, padding: const EdgeInsets.only(top: 20.0)));
|
||||||
final RenderFlex row = new RenderFlex(direction: Axis.horizontal);
|
final RenderFlex row = new RenderFlex(direction: Axis.horizontal);
|
||||||
row.add(new RenderSolidColorBox(const Color(0xFFFFCCCC), desiredSize: const Size(80.0, 60.0)));
|
row.add(new RenderSolidColorBox(const Color(0xFFFFCCCC), desiredSize: const Size(80.0, 60.0)));
|
||||||
|
@ -16,7 +16,14 @@ void main() {
|
|||||||
alignment: FractionalOffset.center,
|
alignment: FractionalOffset.center,
|
||||||
// We use a RenderParagraph to display the text 'Hello, world.' without
|
// We use a RenderParagraph to display the text 'Hello, world.' without
|
||||||
// any explicit styling.
|
// any explicit styling.
|
||||||
child: new RenderParagraph(const TextSpan(text: 'Hello, world.'))
|
child: new RenderParagraph(
|
||||||
|
const TextSpan(text: 'Hello, world.'),
|
||||||
|
// The text is in English so we specify the text direction as
|
||||||
|
// left-to-right. If the text had been in Hebrew or Arabic, we would
|
||||||
|
// have specified right-to-left. The Flutter framework does not assume a
|
||||||
|
// particular text direction.
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -104,8 +104,9 @@ void main() {
|
|||||||
final RenderParagraph paragraph = new RenderParagraph(
|
final RenderParagraph paragraph = new RenderParagraph(
|
||||||
const TextSpan(
|
const TextSpan(
|
||||||
style: const TextStyle(color: Colors.black87),
|
style: const TextStyle(color: Colors.black87),
|
||||||
text: "Touch me!"
|
text: "Touch me!",
|
||||||
)
|
),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
// A stack is a render object that layers its children on top of each other.
|
// A stack is a render object that layers its children on top of each other.
|
||||||
// The bottom later is our RenderDots object, and on top of that we show the
|
// The bottom later is our RenderDots object, and on top of that we show the
|
||||||
@ -114,7 +115,7 @@ void main() {
|
|||||||
children: <RenderBox>[
|
children: <RenderBox>[
|
||||||
new RenderDots(),
|
new RenderDots(),
|
||||||
paragraph,
|
paragraph,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
// The "parentData" field of a render object is controlled by the render
|
// The "parentData" field of a render object is controlled by the render
|
||||||
// object's parent render object. Now that we've added the paragraph as a
|
// object's parent render object. Now that we've added the paragraph as a
|
||||||
|
13
examples/layers/test/smoketests/lib/main_test.dart
Normal file
13
examples/layers/test/smoketests/lib/main_test.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../lib/main.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for lib/main.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
13
examples/layers/test/smoketests/raw/canvas_test.dart
Normal file
13
examples/layers/test/smoketests/raw/canvas_test.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../raw/canvas.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for raw/canvas.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
13
examples/layers/test/smoketests/raw/hello_world_test.dart
Normal file
13
examples/layers/test/smoketests/raw/hello_world_test.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../raw/hello_world.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for raw/hello_world.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../raw/spinning_square.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for raw/spinning_square.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
13
examples/layers/test/smoketests/raw/text_test.dart
Normal file
13
examples/layers/test/smoketests/raw/text_test.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../raw/text.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for raw/text.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
13
examples/layers/test/smoketests/raw/touch_input_test.dart
Normal file
13
examples/layers/test/smoketests/raw/touch_input_test.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../raw/touch_input.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for raw/touch_input.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../rendering/custom_coordinate_systems.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for rendering/custom_coordinate_systems.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../rendering/flex_layout.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for rendering/flex_layout.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../rendering/hello_world.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for rendering/hello_world.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../rendering/spinning_square.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for rendering/spinning_square.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../rendering/touch_input.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for rendering/touch_input.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
13
examples/layers/test/smoketests/services/isolate_test.dart
Normal file
13
examples/layers/test/smoketests/services/isolate_test.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../services/isolate.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for services/isolate.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
13
examples/layers/test/smoketests/services/lifecycle_test.dart
Normal file
13
examples/layers/test/smoketests/services/lifecycle_test.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../services/lifecycle.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for services/lifecycle.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../widgets/custom_render_box.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for widgets/custom_render_box.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
13
examples/layers/test/smoketests/widgets/gestures_test.dart
Normal file
13
examples/layers/test/smoketests/widgets/gestures_test.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../widgets/gestures.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for widgets/gestures.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../widgets/hello_world.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for widgets/hello_world.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../widgets/media_query.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for widgets/media_query.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
13
examples/layers/test/smoketests/widgets/sectors_test.dart
Normal file
13
examples/layers/test/smoketests/widgets/sectors_test.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../widgets/sectors.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for widgets/sectors.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../widgets/spinning_mixed.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for widgets/spinning_mixed.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../widgets/spinning_square.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for widgets/spinning_square.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../../widgets/styled_text.dart' as demo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('layers smoketest for widgets/styled_text.dart', () {
|
||||||
|
demo.main();
|
||||||
|
});
|
||||||
|
}
|
@ -4,4 +4,4 @@
|
|||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
void main() => runApp(const Center(child: const Text('Hello, world!')));
|
void main() => runApp(const Center(child: const Text('Hello, world!', textDirection: TextDirection.ltr)));
|
||||||
|
@ -37,6 +37,8 @@ BuildOwner owner = new BuildOwner();
|
|||||||
void attachWidgetTreeToRenderTree(RenderProxyBox container) {
|
void attachWidgetTreeToRenderTree(RenderProxyBox container) {
|
||||||
element = new RenderObjectToWidgetAdapter<RenderBox>(
|
element = new RenderObjectToWidgetAdapter<RenderBox>(
|
||||||
container: container,
|
container: container,
|
||||||
|
child: new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
child: new Container(
|
child: new Container(
|
||||||
height: 300.0,
|
height: 300.0,
|
||||||
child: new Column(
|
child: new Column(
|
||||||
@ -53,24 +55,25 @@ void attachWidgetTreeToRenderTree(RenderProxyBox container) {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Image.network('https://flutter.io/images/favicon.png'),
|
new Image.network('https://flutter.io/images/favicon.png'),
|
||||||
const Text('PRESS ME'),
|
const Text('PRESS ME'),
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
value = value == null ? 0.1 : (value + 0.1) % 1.0;
|
value = value == null ? 0.1 : (value + 0.1) % 1.0;
|
||||||
attachWidgetTreeToRenderTree(container);
|
attachWidgetTreeToRenderTree(container);
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
new CircularProgressIndicator(value: value),
|
new CircularProgressIndicator(value: value),
|
||||||
],
|
],
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
const Rectangle(const Color(0xFFFFFF00)),
|
const Rectangle(const Color(0xFFFFFF00)),
|
||||||
],
|
],
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
).attachToRenderTree(owner, element);
|
).attachToRenderTree(owner, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,3 +46,4 @@ export 'src/foundation/print.dart';
|
|||||||
export 'src/foundation/profile.dart';
|
export 'src/foundation/profile.dart';
|
||||||
export 'src/foundation/serialization.dart';
|
export 'src/foundation/serialization.dart';
|
||||||
export 'src/foundation/synchronous_future.dart';
|
export 'src/foundation/synchronous_future.dart';
|
||||||
|
export 'src/foundation/unicode.dart';
|
||||||
|
98
packages/flutter/lib/src/foundation/unicode.dart
Normal file
98
packages/flutter/lib/src/foundation/unicode.dart
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
/// Constants for useful Unicode characters.
|
||||||
|
///
|
||||||
|
/// Currently, these characters are all related to bidirectional text.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * <http://unicode.org/reports/tr9/>, which describes the Unicode
|
||||||
|
/// bidirectional text algorithm.
|
||||||
|
class Unicode {
|
||||||
|
Unicode._();
|
||||||
|
/// U+202A LEFT-TO-RIGHT EMBEDDING
|
||||||
|
///
|
||||||
|
/// Treat the following text as embedded left-to-right.
|
||||||
|
///
|
||||||
|
/// Use [PDF] to end the embedding.
|
||||||
|
static const String LRE = '\u202A';
|
||||||
|
|
||||||
|
/// U+202B RIGHT-TO-LEFT EMBEDDING
|
||||||
|
///
|
||||||
|
/// Treat the following text as embedded right-to-left.
|
||||||
|
///
|
||||||
|
/// Use [PDF] to end the embedding.
|
||||||
|
static const String RLE = '\u202B';
|
||||||
|
|
||||||
|
/// U+202C POP DIRECTIONAL FORMATTING
|
||||||
|
///
|
||||||
|
/// End the scope of the last [LRE], [RLE], [RLO], or [LRO].
|
||||||
|
static const String PDF = '\u202C';
|
||||||
|
|
||||||
|
/// U+202A LEFT-TO-RIGHT OVERRIDE
|
||||||
|
///
|
||||||
|
/// Force following characters to be treated as strong left-to-right characters.
|
||||||
|
///
|
||||||
|
/// For example, this causes Hebrew text to render backwards.
|
||||||
|
///
|
||||||
|
/// Use [PDF] to end the override.
|
||||||
|
static const String LRO = '\u202D';
|
||||||
|
|
||||||
|
/// U+202B RIGHT-TO-LEFT OVERRIDE
|
||||||
|
///
|
||||||
|
/// Force following characters to be treated as strong right-to-left characters.
|
||||||
|
///
|
||||||
|
/// For example, this causes English text to render backwards.
|
||||||
|
///
|
||||||
|
/// Use [PDF] to end the override.
|
||||||
|
static const String RLO = '\u202E';
|
||||||
|
|
||||||
|
/// U+2066 LEFT-TO-RIGHT ISOLATE
|
||||||
|
///
|
||||||
|
/// Treat the following text as isolated and left-to-right.
|
||||||
|
///
|
||||||
|
/// Use [PDI] to end the isolated scope.
|
||||||
|
static const String LRI = '\u2066';
|
||||||
|
|
||||||
|
/// U+2067 RIGHT-TO-LEFT ISOLATE
|
||||||
|
///
|
||||||
|
/// Treat the following text as isolated and right-to-left.
|
||||||
|
///
|
||||||
|
/// Use [PDI] to end the isolated scope.
|
||||||
|
static const String RLI = '\u2067';
|
||||||
|
|
||||||
|
/// U+2068 FIRST STRONG ISOLATE
|
||||||
|
///
|
||||||
|
/// Treat the following text as isolated and in the direction of its first
|
||||||
|
/// strong directional character that is not inside a nested isolate.
|
||||||
|
///
|
||||||
|
/// This essentially "autodetects" the directionality of the text. It is not
|
||||||
|
/// 100% reliable. For example, Arabic text that starts with an English quote
|
||||||
|
/// will be detected as LTR, not RTL, which will lead to the text being in a
|
||||||
|
/// weird order.
|
||||||
|
///
|
||||||
|
/// Use [PDI] to end the isolated scope.
|
||||||
|
static const String FSI = '\u2068';
|
||||||
|
|
||||||
|
/// U+2069 POP DIRECTIONAL ISOLATE
|
||||||
|
///
|
||||||
|
/// End the scope of the last [LRI], [RLI], or [FSI].
|
||||||
|
static const String PDI = '\u2069';
|
||||||
|
|
||||||
|
/// U+200E LEFT-TO-RIGHT MARK
|
||||||
|
///
|
||||||
|
/// Left-to-right zero-width character.
|
||||||
|
static const String LRM = '\u200E';
|
||||||
|
|
||||||
|
/// U+200F RIGHT-TO-LEFT MARK
|
||||||
|
///
|
||||||
|
/// Right-to-left zero-width non-Arabic character.
|
||||||
|
static const String RLM = '\u200F';
|
||||||
|
|
||||||
|
/// U+061C ARABIC LETTER MARK
|
||||||
|
///
|
||||||
|
/// Right-to-left zero-width Arabic character.
|
||||||
|
static const String ALM = '\u061C';
|
||||||
|
}
|
@ -295,6 +295,7 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
|
|||||||
@required TextDirection textDirection,
|
@required TextDirection textDirection,
|
||||||
}) : assert(value != null && value >= 0.0 && value <= 1.0),
|
}) : assert(value != null && value >= 0.0 && value <= 1.0),
|
||||||
assert(textDirection != null),
|
assert(textDirection != null),
|
||||||
|
_label = label,
|
||||||
_value = value,
|
_value = value,
|
||||||
_divisions = divisions,
|
_divisions = divisions,
|
||||||
_activeColor = activeColor,
|
_activeColor = activeColor,
|
||||||
@ -303,7 +304,7 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
|
|||||||
_textTheme = textTheme,
|
_textTheme = textTheme,
|
||||||
_onChanged = onChanged,
|
_onChanged = onChanged,
|
||||||
_textDirection = textDirection {
|
_textDirection = textDirection {
|
||||||
this.label = label;
|
_updateLabelPainter();
|
||||||
final GestureArenaTeam team = new GestureArenaTeam();
|
final GestureArenaTeam team = new GestureArenaTeam();
|
||||||
_drag = new HorizontalDragGestureRecognizer()
|
_drag = new HorizontalDragGestureRecognizer()
|
||||||
..team = team
|
..team = team
|
||||||
@ -356,19 +357,7 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
|
|||||||
if (value == _label)
|
if (value == _label)
|
||||||
return;
|
return;
|
||||||
_label = value;
|
_label = value;
|
||||||
if (value != null) {
|
_updateLabelPainter();
|
||||||
// TODO(abarth): Handle textScaleFactor.
|
|
||||||
// https://github.com/flutter/flutter/issues/5938
|
|
||||||
_labelPainter
|
|
||||||
..text = new TextSpan(
|
|
||||||
style: _textTheme.body1.copyWith(fontSize: 10.0),
|
|
||||||
text: value
|
|
||||||
)
|
|
||||||
..layout();
|
|
||||||
} else {
|
|
||||||
_labelPainter.text = null;
|
|
||||||
}
|
|
||||||
markNeedsLayout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Color get activeColor => _activeColor;
|
Color get activeColor => _activeColor;
|
||||||
@ -424,11 +413,29 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
|
|||||||
TextDirection _textDirection;
|
TextDirection _textDirection;
|
||||||
set textDirection(TextDirection value) {
|
set textDirection(TextDirection value) {
|
||||||
assert(value != null);
|
assert(value != null);
|
||||||
if (_textDirection == value)
|
if (value == _textDirection)
|
||||||
return;
|
return;
|
||||||
_textDirection = value;
|
_textDirection = value;
|
||||||
// TODO(abarth): Update _labelPainter's text direction.
|
_updateLabelPainter();
|
||||||
markNeedsPaint();
|
}
|
||||||
|
|
||||||
|
void _updateLabelPainter() {
|
||||||
|
if (label != null) {
|
||||||
|
// TODO(abarth): Handle textScaleFactor. https://github.com/flutter/flutter/issues/5938
|
||||||
|
_labelPainter
|
||||||
|
..text = new TextSpan(
|
||||||
|
style: _textTheme.body1.copyWith(fontSize: 10.0),
|
||||||
|
text: label,
|
||||||
|
)
|
||||||
|
..textDirection = textDirection
|
||||||
|
..layout();
|
||||||
|
} else {
|
||||||
|
_labelPainter.text = null;
|
||||||
|
}
|
||||||
|
// Changing the textDirection can result in the layout changing, because the
|
||||||
|
// bidi algorithm might line up the glyphs differently which can result in
|
||||||
|
// different ligatures, different shapes, etc. So we always markNeedsLayout.
|
||||||
|
markNeedsLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
double get _trackLength => size.width - 2.0 * _kReactionRadius;
|
double get _trackLength => size.width - 2.0 * _kReactionRadius;
|
||||||
@ -633,7 +640,6 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
|
|||||||
..lineTo(center.dx + tipAttachment, center.dy + tipAttachment)
|
..lineTo(center.dx + tipAttachment, center.dy + tipAttachment)
|
||||||
..close();
|
..close();
|
||||||
canvas.drawPath(path, primaryPaint);
|
canvas.drawPath(path, primaryPaint);
|
||||||
_labelPainter.layout();
|
|
||||||
final Offset labelOffset = new Offset(
|
final Offset labelOffset = new Offset(
|
||||||
center.dx - _labelPainter.width / 2.0,
|
center.dx - _labelPainter.width / 2.0,
|
||||||
center.dy - _labelPainter.height / 2.0
|
center.dy - _labelPainter.height / 2.0
|
||||||
|
@ -69,8 +69,8 @@ class TextField extends StatefulWidget {
|
|||||||
/// the number of lines. By default, it is 1, meaning this is a single-line
|
/// the number of lines. By default, it is 1, meaning this is a single-line
|
||||||
/// text field. If it is not null, it must be greater than zero.
|
/// text field. If it is not null, it must be greater than zero.
|
||||||
///
|
///
|
||||||
/// The [keyboardType], [autofocus], [obscureText], and [autocorrect] arguments
|
/// The [keyboardType], [textAlign], [autofocus], [obscureText], and
|
||||||
/// must not be null.
|
/// [autocorrect] arguments must not be null.
|
||||||
const TextField({
|
const TextField({
|
||||||
Key key,
|
Key key,
|
||||||
this.controller,
|
this.controller,
|
||||||
@ -78,7 +78,7 @@ class TextField extends StatefulWidget {
|
|||||||
this.decoration: const InputDecoration(),
|
this.decoration: const InputDecoration(),
|
||||||
this.keyboardType: TextInputType.text,
|
this.keyboardType: TextInputType.text,
|
||||||
this.style,
|
this.style,
|
||||||
this.textAlign,
|
this.textAlign: TextAlign.start,
|
||||||
this.autofocus: false,
|
this.autofocus: false,
|
||||||
this.obscureText: false,
|
this.obscureText: false,
|
||||||
this.autocorrect: true,
|
this.autocorrect: true,
|
||||||
@ -87,6 +87,7 @@ class TextField extends StatefulWidget {
|
|||||||
this.onSubmitted,
|
this.onSubmitted,
|
||||||
this.inputFormatters,
|
this.inputFormatters,
|
||||||
}) : assert(keyboardType != null),
|
}) : assert(keyboardType != null),
|
||||||
|
assert(textAlign != null),
|
||||||
assert(autofocus != null),
|
assert(autofocus != null),
|
||||||
assert(obscureText != null),
|
assert(obscureText != null),
|
||||||
assert(autocorrect != null),
|
assert(autocorrect != null),
|
||||||
@ -125,6 +126,8 @@ class TextField extends StatefulWidget {
|
|||||||
final TextStyle style;
|
final TextStyle style;
|
||||||
|
|
||||||
/// How the text being edited should be aligned horizontally.
|
/// How the text being edited should be aligned horizontally.
|
||||||
|
///
|
||||||
|
/// Defaults to [TextAlign.start].
|
||||||
final TextAlign textAlign;
|
final TextAlign textAlign;
|
||||||
|
|
||||||
/// Whether this text field should focus itself if nothing else is already
|
/// Whether this text field should focus itself if nothing else is already
|
||||||
|
@ -356,7 +356,8 @@ List<TextPainter> _initPainters(TextTheme textTheme, List<String> labels) {
|
|||||||
// TODO(abarth): Handle textScaleFactor.
|
// TODO(abarth): Handle textScaleFactor.
|
||||||
// https://github.com/flutter/flutter/issues/5939
|
// https://github.com/flutter/flutter/issues/5939
|
||||||
painters[i] = new TextPainter(
|
painters[i] = new TextPainter(
|
||||||
text: new TextSpan(style: style, text: label)
|
text: new TextSpan(style: style, text: label),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
)..layout();
|
)..layout();
|
||||||
}
|
}
|
||||||
return painters;
|
return painters;
|
||||||
|
@ -247,9 +247,10 @@ class _FlutterLogoPainter extends BoxPainter {
|
|||||||
fontFamily: 'Roboto',
|
fontFamily: 'Roboto',
|
||||||
fontSize: 100.0 * 350.0 / 247.0, // 247 is the height of the F when the fontSize is 350, assuming device pixel ratio 1.0
|
fontSize: 100.0 * 350.0 / 247.0, // 247 is the height of the F when the fontSize is 350, assuming device pixel ratio 1.0
|
||||||
fontWeight: FontWeight.w300,
|
fontWeight: FontWeight.w300,
|
||||||
textBaseline: TextBaseline.alphabetic
|
textBaseline: TextBaseline.alphabetic,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
_textPainter.layout();
|
_textPainter.layout();
|
||||||
final ui.TextBox textSize = _textPainter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: kLabel.length)).single;
|
final ui.TextBox textSize = _textPainter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: kLabel.length)).single;
|
||||||
|
@ -33,21 +33,26 @@ final String _kZeroWidthSpace = new String.fromCharCode(0x200B);
|
|||||||
class TextPainter {
|
class TextPainter {
|
||||||
/// Creates a text painter that paints the given text.
|
/// Creates a text painter that paints the given text.
|
||||||
///
|
///
|
||||||
/// The text argument is optional but [text] must be non-null before calling
|
/// The `text` and `textDirection` arguments are optional but [text] and
|
||||||
/// [layout].
|
/// [textDirection] must be non-null before calling [layout].
|
||||||
|
///
|
||||||
|
/// The [textAlign] property must not be null.
|
||||||
///
|
///
|
||||||
/// The [maxLines] property, if non-null, must be greater than zero.
|
/// The [maxLines] property, if non-null, must be greater than zero.
|
||||||
TextPainter({
|
TextPainter({
|
||||||
TextSpan text,
|
TextSpan text,
|
||||||
TextAlign textAlign,
|
TextAlign textAlign: TextAlign.start,
|
||||||
|
TextDirection textDirection,
|
||||||
double textScaleFactor: 1.0,
|
double textScaleFactor: 1.0,
|
||||||
int maxLines,
|
int maxLines,
|
||||||
String ellipsis,
|
String ellipsis,
|
||||||
}) : assert(text == null || text.debugAssertIsValid()),
|
}) : assert(text == null || text.debugAssertIsValid()),
|
||||||
|
assert(textAlign != null),
|
||||||
assert(textScaleFactor != null),
|
assert(textScaleFactor != null),
|
||||||
assert(maxLines == null || maxLines > 0),
|
assert(maxLines == null || maxLines > 0),
|
||||||
_text = text,
|
_text = text,
|
||||||
_textAlign = textAlign,
|
_textAlign = textAlign,
|
||||||
|
_textDirection = textDirection,
|
||||||
_textScaleFactor = textScaleFactor,
|
_textScaleFactor = textScaleFactor,
|
||||||
_maxLines = maxLines,
|
_maxLines = maxLines,
|
||||||
_ellipsis = ellipsis;
|
_ellipsis = ellipsis;
|
||||||
@ -58,6 +63,8 @@ class TextPainter {
|
|||||||
/// The (potentially styled) text to paint.
|
/// The (potentially styled) text to paint.
|
||||||
///
|
///
|
||||||
/// After this is set, you must call [layout] before the next call to [paint].
|
/// After this is set, you must call [layout] before the next call to [paint].
|
||||||
|
///
|
||||||
|
/// This and [textDirection] must be non-null before you call [layout].
|
||||||
TextSpan get text => _text;
|
TextSpan get text => _text;
|
||||||
TextSpan _text;
|
TextSpan _text;
|
||||||
set text(TextSpan value) {
|
set text(TextSpan value) {
|
||||||
@ -74,9 +81,12 @@ class TextPainter {
|
|||||||
/// How the text should be aligned horizontally.
|
/// How the text should be aligned horizontally.
|
||||||
///
|
///
|
||||||
/// After this is set, you must call [layout] before the next call to [paint].
|
/// After this is set, you must call [layout] before the next call to [paint].
|
||||||
|
///
|
||||||
|
/// The [textAlign] property must not be null. It defaults to [TextAlign.start].
|
||||||
TextAlign get textAlign => _textAlign;
|
TextAlign get textAlign => _textAlign;
|
||||||
TextAlign _textAlign;
|
TextAlign _textAlign;
|
||||||
set textAlign(TextAlign value) {
|
set textAlign(TextAlign value) {
|
||||||
|
assert(value != null);
|
||||||
if (_textAlign == value)
|
if (_textAlign == value)
|
||||||
return;
|
return;
|
||||||
_textAlign = value;
|
_textAlign = value;
|
||||||
@ -84,6 +94,32 @@ class TextPainter {
|
|||||||
_needsLayout = true;
|
_needsLayout = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The default directionality of the text.
|
||||||
|
///
|
||||||
|
/// This controls how the [TextAlign.start], [TextAlign.end], and
|
||||||
|
/// [TextAlign.justify] values of [textAlign] are resolved.
|
||||||
|
///
|
||||||
|
/// This is also used to disambiguate how to render bidirectional text. For
|
||||||
|
/// example, if the [text] is an English phrase followed by a Hebrew phrase,
|
||||||
|
/// in a [TextDirection.ltr] context the English phrase will be on the left
|
||||||
|
/// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
|
||||||
|
/// context, the English phrase will be on the right and the Hebrow phrase on
|
||||||
|
/// its left.
|
||||||
|
///
|
||||||
|
/// After this is set, you must call [layout] before the next call to [paint].
|
||||||
|
///
|
||||||
|
/// This and [text] must be non-null before you call [layout].
|
||||||
|
TextDirection get textDirection => _textDirection;
|
||||||
|
TextDirection _textDirection;
|
||||||
|
set textDirection(TextDirection value) {
|
||||||
|
if (_textDirection == value)
|
||||||
|
return;
|
||||||
|
_textDirection = value;
|
||||||
|
_paragraph = null;
|
||||||
|
_layoutTemplate = null; // Shouldn't really matter, but for strict correctness...
|
||||||
|
_needsLayout = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// The number of font pixels for each logical pixel.
|
/// The number of font pixels for each logical pixel.
|
||||||
///
|
///
|
||||||
/// For example, if the text scale factor is 1.5, text will be 50% larger than
|
/// For example, if the text scale factor is 1.5, text will be 50% larger than
|
||||||
@ -149,14 +185,20 @@ class TextPainter {
|
|||||||
|
|
||||||
ui.Paragraph _layoutTemplate;
|
ui.Paragraph _layoutTemplate;
|
||||||
|
|
||||||
ui.ParagraphStyle _createParagraphStyle() {
|
ui.ParagraphStyle _createParagraphStyle([TextDirection defaultTextDirection]) {
|
||||||
|
// The defaultTextDirection argument is used for preferredLineHeight in case
|
||||||
|
// textDirection hasn't yet been set.
|
||||||
|
assert(textAlign != null);
|
||||||
|
assert(textDirection != null || defaultTextDirection != null, 'TextPainter.textDirection must be set to a non-null value before using the TextPainter.');
|
||||||
return _text.style?.getParagraphStyle(
|
return _text.style?.getParagraphStyle(
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
|
textDirection: textDirection ?? defaultTextDirection,
|
||||||
textScaleFactor: textScaleFactor,
|
textScaleFactor: textScaleFactor,
|
||||||
maxLines: _maxLines,
|
maxLines: _maxLines,
|
||||||
ellipsis: _ellipsis,
|
ellipsis: _ellipsis,
|
||||||
) ?? new ui.ParagraphStyle(
|
) ?? new ui.ParagraphStyle(
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
|
textDirection: textDirection ?? defaultTextDirection,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
ellipsis: ellipsis,
|
ellipsis: ellipsis,
|
||||||
);
|
);
|
||||||
@ -169,11 +211,17 @@ class TextPainter {
|
|||||||
/// relative a typical line of text.
|
/// relative a typical line of text.
|
||||||
///
|
///
|
||||||
/// Obtaining this value does not require calling [layout].
|
/// Obtaining this value does not require calling [layout].
|
||||||
|
///
|
||||||
|
/// The style of the [text] property is used to determine the font settings
|
||||||
|
/// that contribute to the [preferredLineHeight]. If [text] is null or if it
|
||||||
|
/// specifies no styles, the default [TextStyle] values are used (a 10 pixel
|
||||||
|
/// sans-serif font).
|
||||||
double get preferredLineHeight {
|
double get preferredLineHeight {
|
||||||
assert(text != null);
|
|
||||||
if (_layoutTemplate == null) {
|
if (_layoutTemplate == null) {
|
||||||
final ui.ParagraphBuilder builder = new ui.ParagraphBuilder(_createParagraphStyle());
|
final ui.ParagraphBuilder builder = new ui.ParagraphBuilder(
|
||||||
if (text.style != null)
|
_createParagraphStyle(TextDirection.rtl),
|
||||||
|
); // direction doesn't matter, text is just a zero width space
|
||||||
|
if (text?.style != null)
|
||||||
builder.pushStyle(text.style.getTextStyle(textScaleFactor: textScaleFactor));
|
builder.pushStyle(text.style.getTextStyle(textScaleFactor: textScaleFactor));
|
||||||
builder.addText(_kZeroWidthSpace);
|
builder.addText(_kZeroWidthSpace);
|
||||||
_layoutTemplate = builder.build()
|
_layoutTemplate = builder.build()
|
||||||
@ -272,10 +320,14 @@ class TextPainter {
|
|||||||
/// Computes the visual position of the glyphs for painting the text.
|
/// Computes the visual position of the glyphs for painting the text.
|
||||||
///
|
///
|
||||||
/// The text will layout with a width that's as close to its max intrinsic
|
/// The text will layout with a width that's as close to its max intrinsic
|
||||||
/// width as possible while still being greater than or equal to minWidth and
|
/// width as possible while still being greater than or equal to `minWidth` and
|
||||||
/// less than or equal to maxWidth.
|
/// less than or equal to `maxWidth`.
|
||||||
|
///
|
||||||
|
/// The [text] and [textDirection] properties must be non-null before this is
|
||||||
|
/// called.
|
||||||
void layout({ double minWidth: 0.0, double maxWidth: double.INFINITY }) {
|
void layout({ double minWidth: 0.0, double maxWidth: double.INFINITY }) {
|
||||||
assert(_text != null);
|
assert(text != null, 'TextPainter.text must be set to a non-null value before using the TextPainter.');
|
||||||
|
assert(textDirection != null, 'TextPainter.textDirection must be set to a non-null value before using the TextPainter.');
|
||||||
if (!_needsLayout && minWidth == _lastMinWidth && maxWidth == _lastMaxWidth)
|
if (!_needsLayout && minWidth == _lastMinWidth && maxWidth == _lastMaxWidth)
|
||||||
return;
|
return;
|
||||||
_needsLayout = false;
|
_needsLayout = false;
|
||||||
@ -352,15 +404,34 @@ class TextPainter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Offset get _emptyOffset {
|
Offset get _emptyOffset {
|
||||||
// TODO(abarth): Handle the directionality of the text painter itself.
|
assert(!_needsLayout); // implies textDirection is non-null
|
||||||
switch (textAlign ?? TextAlign.left) {
|
assert(textAlign != null);
|
||||||
|
switch (textAlign) {
|
||||||
case TextAlign.left:
|
case TextAlign.left:
|
||||||
case TextAlign.justify:
|
|
||||||
return Offset.zero;
|
return Offset.zero;
|
||||||
case TextAlign.right:
|
case TextAlign.right:
|
||||||
return new Offset(width, 0.0);
|
return new Offset(width, 0.0);
|
||||||
case TextAlign.center:
|
case TextAlign.center:
|
||||||
return new Offset(width / 2.0, 0.0);
|
return new Offset(width / 2.0, 0.0);
|
||||||
|
case TextAlign.justify:
|
||||||
|
case TextAlign.start:
|
||||||
|
assert(textDirection != null);
|
||||||
|
switch (textDirection) {
|
||||||
|
case TextDirection.rtl:
|
||||||
|
return new Offset(width, 0.0);
|
||||||
|
case TextDirection.ltr:
|
||||||
|
return Offset.zero;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
case TextAlign.end:
|
||||||
|
assert(textDirection != null);
|
||||||
|
switch (textDirection) {
|
||||||
|
case TextDirection.rtl:
|
||||||
|
return Offset.zero;
|
||||||
|
case TextDirection.ltr:
|
||||||
|
return new Offset(width, 0.0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -369,6 +369,7 @@ class TextStyle extends Diagnosticable {
|
|||||||
/// specified and non-null, must be greater than zero.
|
/// specified and non-null, must be greater than zero.
|
||||||
ui.ParagraphStyle getParagraphStyle({
|
ui.ParagraphStyle getParagraphStyle({
|
||||||
TextAlign textAlign,
|
TextAlign textAlign,
|
||||||
|
TextDirection textDirection,
|
||||||
double textScaleFactor: 1.0,
|
double textScaleFactor: 1.0,
|
||||||
String ellipsis,
|
String ellipsis,
|
||||||
int maxLines,
|
int maxLines,
|
||||||
@ -377,6 +378,7 @@ class TextStyle extends Diagnosticable {
|
|||||||
assert(maxLines == null || maxLines > 0);
|
assert(maxLines == null || maxLines > 0);
|
||||||
return new ui.ParagraphStyle(
|
return new ui.ParagraphStyle(
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
|
textDirection: textDirection,
|
||||||
fontWeight: fontWeight,
|
fontWeight: fontWeight,
|
||||||
fontStyle: fontStyle,
|
fontStyle: fontStyle,
|
||||||
fontFamily: fontFamily,
|
fontFamily: fontFamily,
|
||||||
@ -418,7 +420,7 @@ class TextStyle extends Diagnosticable {
|
|||||||
bool operator ==(dynamic other) {
|
bool operator ==(dynamic other) {
|
||||||
if (identical(this, other))
|
if (identical(this, other))
|
||||||
return true;
|
return true;
|
||||||
if (other is! TextStyle)
|
if (other.runtimeType != runtimeType)
|
||||||
return false;
|
return false;
|
||||||
final TextStyle typedOther = other;
|
final TextStyle typedOther = other;
|
||||||
return inherit == typedOther.inherit &&
|
return inherit == typedOther.inherit &&
|
||||||
|
@ -87,6 +87,10 @@ class TextSelectionPoint {
|
|||||||
class RenderEditable extends RenderBox {
|
class RenderEditable extends RenderBox {
|
||||||
/// Creates a render object that implements the visual aspects of a text field.
|
/// Creates a render object that implements the visual aspects of a text field.
|
||||||
///
|
///
|
||||||
|
/// The [textAlign] argument must not be null. It defaults to [TextAlign.start].
|
||||||
|
///
|
||||||
|
/// The [textDirection] argument must not be null.
|
||||||
|
///
|
||||||
/// If [showCursor] is not specified, then it defaults to hiding the cursor.
|
/// If [showCursor] is not specified, then it defaults to hiding the cursor.
|
||||||
///
|
///
|
||||||
/// The [maxLines] property can be set to null to remove the restriction on
|
/// The [maxLines] property can be set to null to remove the restriction on
|
||||||
@ -97,7 +101,8 @@ class RenderEditable extends RenderBox {
|
|||||||
/// ViewportOffset.zero] if you have no need for scrolling.
|
/// ViewportOffset.zero] if you have no need for scrolling.
|
||||||
RenderEditable({
|
RenderEditable({
|
||||||
TextSpan text,
|
TextSpan text,
|
||||||
TextAlign textAlign,
|
@required TextDirection textDirection,
|
||||||
|
TextAlign textAlign: TextAlign.start,
|
||||||
Color cursorColor,
|
Color cursorColor,
|
||||||
ValueNotifier<bool> showCursor,
|
ValueNotifier<bool> showCursor,
|
||||||
int maxLines: 1,
|
int maxLines: 1,
|
||||||
@ -107,10 +112,17 @@ class RenderEditable extends RenderBox {
|
|||||||
@required ViewportOffset offset,
|
@required ViewportOffset offset,
|
||||||
this.onSelectionChanged,
|
this.onSelectionChanged,
|
||||||
this.onCaretChanged,
|
this.onCaretChanged,
|
||||||
}) : assert(maxLines == null || maxLines > 0),
|
}) : assert(textAlign != null),
|
||||||
|
assert(textDirection != null, 'RenderEditable created without a textDirection.'),
|
||||||
|
assert(maxLines == null || maxLines > 0),
|
||||||
assert(textScaleFactor != null),
|
assert(textScaleFactor != null),
|
||||||
assert(offset != null),
|
assert(offset != null),
|
||||||
_textPainter = new TextPainter(text: text, textAlign: textAlign, textScaleFactor: textScaleFactor),
|
_textPainter = new TextPainter(
|
||||||
|
text: text,
|
||||||
|
textAlign: textAlign,
|
||||||
|
textDirection: textDirection,
|
||||||
|
textScaleFactor: textScaleFactor,
|
||||||
|
),
|
||||||
_cursorColor = cursorColor,
|
_cursorColor = cursorColor,
|
||||||
_showCursor = showCursor ?? new ValueNotifier<bool>(false),
|
_showCursor = showCursor ?? new ValueNotifier<bool>(false),
|
||||||
_maxLines = maxLines,
|
_maxLines = maxLines,
|
||||||
@ -146,7 +158,7 @@ class RenderEditable extends RenderBox {
|
|||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The text to display
|
/// The text to display.
|
||||||
TextSpan get text => _textPainter.text;
|
TextSpan get text => _textPainter.text;
|
||||||
final TextPainter _textPainter;
|
final TextPainter _textPainter;
|
||||||
set text(TextSpan value) {
|
set text(TextSpan value) {
|
||||||
@ -157,14 +169,39 @@ class RenderEditable extends RenderBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// How the text should be aligned horizontally.
|
/// How the text should be aligned horizontally.
|
||||||
|
///
|
||||||
|
/// This must not be null.
|
||||||
TextAlign get textAlign => _textPainter.textAlign;
|
TextAlign get textAlign => _textPainter.textAlign;
|
||||||
set textAlign(TextAlign value) {
|
set textAlign(TextAlign value) {
|
||||||
|
assert(value != null);
|
||||||
if (_textPainter.textAlign == value)
|
if (_textPainter.textAlign == value)
|
||||||
return;
|
return;
|
||||||
_textPainter.textAlign = value;
|
_textPainter.textAlign = value;
|
||||||
markNeedsPaint();
|
markNeedsPaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The directionality of the text.
|
||||||
|
///
|
||||||
|
/// This decides how the [TextAlign.start], [TextAlign.end], and
|
||||||
|
/// [TextAlign.justify] values of [textAlign] are interpreted.
|
||||||
|
///
|
||||||
|
/// This is also used to disambiguate how to render bidirectional text. For
|
||||||
|
/// example, if the [text] is an English phrase followed by a Hebrew phrase,
|
||||||
|
/// in a [TextDirection.ltr] context the English phrase will be on the left
|
||||||
|
/// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
|
||||||
|
/// context, the English phrase will be on the right and the Hebrow phrase on
|
||||||
|
/// its left.
|
||||||
|
///
|
||||||
|
/// This must not be null.
|
||||||
|
TextDirection get textDirection => _textPainter.textDirection;
|
||||||
|
set textDirection(TextDirection value) {
|
||||||
|
assert(value != null);
|
||||||
|
if (_textPainter.textDirection == value)
|
||||||
|
return;
|
||||||
|
_textPainter.textDirection = value;
|
||||||
|
markNeedsTextLayout();
|
||||||
|
}
|
||||||
|
|
||||||
/// The color to use when painting the cursor.
|
/// The color to use when painting the cursor.
|
||||||
Color get cursorColor => _cursorColor;
|
Color get cursorColor => _cursorColor;
|
||||||
Color _cursorColor;
|
Color _cursorColor;
|
||||||
|
@ -993,12 +993,13 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
|||||||
}
|
}
|
||||||
context.canvas.drawRect(markerRect, _debugMarkerPaint);
|
context.canvas.drawRect(markerRect, _debugMarkerPaint);
|
||||||
|
|
||||||
_debugMarkerLabel ??= new TextPainter();
|
_debugMarkerLabel ??= new TextPainter()
|
||||||
_debugMarkerLabel.text = new TextSpan( // this is a no-op if the label hasn't changed
|
..textDirection = TextDirection.ltr; // This label is in English.
|
||||||
|
_debugMarkerLabel.text = new TextSpan( // This is a no-op if the label hasn't changed.
|
||||||
text: label,
|
text: label,
|
||||||
style: _debugMarkerTextStyle,
|
style: _debugMarkerTextStyle,
|
||||||
);
|
);
|
||||||
_debugMarkerLabel.layout(); // this is a no-op if the label hasn't changed
|
_debugMarkerLabel.layout(); // This is a no-op if the label hasn't changed.
|
||||||
|
|
||||||
// TODO(ianh): RTL support
|
// TODO(ianh): RTL support
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
|
@ -2867,11 +2867,11 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
|
|||||||
@override
|
@override
|
||||||
void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
||||||
description.add(new DiagnosticsProperty<dynamic>('creator', debugCreator, defaultValue: null));
|
description.add(new DiagnosticsProperty<dynamic>('creator', debugCreator, defaultValue: null));
|
||||||
description.add(new DiagnosticsProperty<ParentData>('parentData', parentData, tooltip: _debugCanParentUseSize == true ? "can use size" : null));
|
description.add(new DiagnosticsProperty<ParentData>('parentData', parentData, tooltip: _debugCanParentUseSize == true ? 'can use size' : null, ifNull: 'MISSING'));
|
||||||
description.add(new DiagnosticsProperty<Constraints>('constraints', constraints));
|
description.add(new DiagnosticsProperty<Constraints>('constraints', constraints, ifNull: 'MISSING'));
|
||||||
// don't access it via the "layer" getter since that's only valid when we don't need paint
|
// don't access it via the "layer" getter since that's only valid when we don't need paint
|
||||||
description.add(new DiagnosticsProperty<OffsetLayer>('layer', _layer, defaultValue: null));
|
description.add(new DiagnosticsProperty<OffsetLayer>('layer', _layer, defaultValue: null));
|
||||||
description.add(new DiagnosticsProperty<SemanticsNode>('_semantics', _semantics, defaultValue: null));
|
description.add(new DiagnosticsProperty<SemanticsNode>('semantics node', _semantics, defaultValue: null));
|
||||||
description.add(new FlagProperty(
|
description.add(new FlagProperty(
|
||||||
'isBlockingSemanticsOfPreviouslyPaintedNodes',
|
'isBlockingSemanticsOfPreviouslyPaintedNodes',
|
||||||
value: isBlockingSemanticsOfPreviouslyPaintedNodes,
|
value: isBlockingSemanticsOfPreviouslyPaintedNodes,
|
||||||
|
@ -32,19 +32,22 @@ const String _kEllipsis = '\u2026';
|
|||||||
class RenderParagraph extends RenderBox {
|
class RenderParagraph extends RenderBox {
|
||||||
/// Creates a paragraph render object.
|
/// Creates a paragraph render object.
|
||||||
///
|
///
|
||||||
/// The [text], [overflow], [softWrap], and [textScaleFactor] arguments must
|
/// The [text], [textAlign], [textDirection], [overflow], [softWrap], and
|
||||||
/// not be null.
|
/// [textScaleFactor] arguments must not be null.
|
||||||
///
|
///
|
||||||
/// The [maxLines] property may be null (and indeed defaults to null), but if
|
/// The [maxLines] property may be null (and indeed defaults to null), but if
|
||||||
/// it is not null, it must be greater than zero.
|
/// it is not null, it must be greater than zero.
|
||||||
RenderParagraph(TextSpan text, {
|
RenderParagraph(TextSpan text, {
|
||||||
TextAlign textAlign,
|
TextAlign textAlign: TextAlign.start,
|
||||||
|
@required TextDirection textDirection,
|
||||||
bool softWrap: true,
|
bool softWrap: true,
|
||||||
TextOverflow overflow: TextOverflow.clip,
|
TextOverflow overflow: TextOverflow.clip,
|
||||||
double textScaleFactor: 1.0,
|
double textScaleFactor: 1.0,
|
||||||
int maxLines,
|
int maxLines,
|
||||||
}) : assert(text != null),
|
}) : assert(text != null),
|
||||||
assert(text.debugAssertIsValid()),
|
assert(text.debugAssertIsValid()),
|
||||||
|
assert(textAlign != null),
|
||||||
|
assert(textDirection != null),
|
||||||
assert(softWrap != null),
|
assert(softWrap != null),
|
||||||
assert(overflow != null),
|
assert(overflow != null),
|
||||||
assert(textScaleFactor != null),
|
assert(textScaleFactor != null),
|
||||||
@ -54,6 +57,7 @@ class RenderParagraph extends RenderBox {
|
|||||||
_textPainter = new TextPainter(
|
_textPainter = new TextPainter(
|
||||||
text: text,
|
text: text,
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
|
textDirection: textDirection,
|
||||||
textScaleFactor: textScaleFactor,
|
textScaleFactor: textScaleFactor,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
ellipsis: overflow == TextOverflow.ellipsis ? _kEllipsis : null,
|
ellipsis: overflow == TextOverflow.ellipsis ? _kEllipsis : null,
|
||||||
@ -84,12 +88,35 @@ class RenderParagraph extends RenderBox {
|
|||||||
/// How the text should be aligned horizontally.
|
/// How the text should be aligned horizontally.
|
||||||
TextAlign get textAlign => _textPainter.textAlign;
|
TextAlign get textAlign => _textPainter.textAlign;
|
||||||
set textAlign(TextAlign value) {
|
set textAlign(TextAlign value) {
|
||||||
|
assert(value != null);
|
||||||
if (_textPainter.textAlign == value)
|
if (_textPainter.textAlign == value)
|
||||||
return;
|
return;
|
||||||
_textPainter.textAlign = value;
|
_textPainter.textAlign = value;
|
||||||
markNeedsPaint();
|
markNeedsPaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The directionality of the text.
|
||||||
|
///
|
||||||
|
/// This decides how the [TextAlign.start], [TextAlign.end], and
|
||||||
|
/// [TextAlign.justify] values of [textAlign] are interpreted.
|
||||||
|
///
|
||||||
|
/// This is also used to disambiguate how to render bidirectional text. For
|
||||||
|
/// example, if the [text] is an English phrase followed by a Hebrew phrase,
|
||||||
|
/// in a [TextDirection.ltr] context the English phrase will be on the left
|
||||||
|
/// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
|
||||||
|
/// context, the English phrase will be on the right and the Hebrow phrase on
|
||||||
|
/// its left.
|
||||||
|
///
|
||||||
|
/// This must not be null.
|
||||||
|
TextDirection get textDirection => _textPainter.textDirection;
|
||||||
|
set textDirection(TextDirection value) {
|
||||||
|
assert(value != null);
|
||||||
|
if (_textPainter.textDirection == value)
|
||||||
|
return;
|
||||||
|
_textPainter.textDirection = value;
|
||||||
|
markNeedsLayout();
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether the text should break at soft line breaks.
|
/// Whether the text should break at soft line breaks.
|
||||||
///
|
///
|
||||||
/// If false, the glyphs in the text will be positioned as if there was
|
/// If false, the glyphs in the text will be positioned as if there was
|
||||||
@ -148,10 +175,8 @@ class RenderParagraph extends RenderBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _layoutText({ double minWidth: 0.0, double maxWidth: double.INFINITY }) {
|
void _layoutText({ double minWidth: 0.0, double maxWidth: double.INFINITY }) {
|
||||||
_textPainter.layout(
|
final bool widthMatters = softWrap || overflow == TextOverflow.ellipsis;
|
||||||
minWidth: minWidth,
|
_textPainter.layout(minWidth: minWidth, maxWidth: widthMatters ? maxWidth : double.INFINITY);
|
||||||
maxWidth: _softWrap || _overflow == TextOverflow.ellipsis ? maxWidth : double.INFINITY
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _layoutTextWithConstraints(BoxConstraints constraints) {
|
void _layoutTextWithConstraints(BoxConstraints constraints) {
|
||||||
@ -245,14 +270,24 @@ class RenderParagraph extends RenderBox {
|
|||||||
_overflowShader = null;
|
_overflowShader = null;
|
||||||
break;
|
break;
|
||||||
case TextOverflow.fade:
|
case TextOverflow.fade:
|
||||||
|
assert(textDirection != null);
|
||||||
final TextPainter fadeSizePainter = new TextPainter(
|
final TextPainter fadeSizePainter = new TextPainter(
|
||||||
text: new TextSpan(style: _textPainter.text.style, text: '\u2026'),
|
text: new TextSpan(style: _textPainter.text.style, text: '\u2026'),
|
||||||
textScaleFactor: textScaleFactor
|
textDirection: textDirection,
|
||||||
|
textScaleFactor: textScaleFactor,
|
||||||
)..layout();
|
)..layout();
|
||||||
if (didOverflowWidth) {
|
if (didOverflowWidth) {
|
||||||
final double fadeEnd = size.width;
|
double fadeEnd, fadeStart;
|
||||||
final double fadeStart = fadeEnd - fadeSizePainter.width;
|
switch (textDirection) {
|
||||||
// TODO(abarth): This shader has an LTR bias.
|
case TextDirection.rtl:
|
||||||
|
fadeEnd = 0.0;
|
||||||
|
fadeStart = fadeSizePainter.width;
|
||||||
|
break;
|
||||||
|
case TextDirection.ltr:
|
||||||
|
fadeEnd = size.width;
|
||||||
|
fadeStart = fadeEnd - fadeSizePainter.width;
|
||||||
|
break;
|
||||||
|
}
|
||||||
_overflowShader = new ui.Gradient.linear(
|
_overflowShader = new ui.Gradient.linear(
|
||||||
new Offset(fadeStart, 0.0),
|
new Offset(fadeStart, 0.0),
|
||||||
new Offset(fadeEnd, 0.0),
|
new Offset(fadeEnd, 0.0),
|
||||||
@ -373,10 +408,22 @@ class RenderParagraph extends RenderBox {
|
|||||||
|
|
||||||
void _annotate(SemanticsNode node) {
|
void _annotate(SemanticsNode node) {
|
||||||
node.label = text.toPlainText();
|
node.label = text.toPlainText();
|
||||||
|
node.textDirection = textDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<DiagnosticsNode> debugDescribeChildren() {
|
List<DiagnosticsNode> debugDescribeChildren() {
|
||||||
return <DiagnosticsNode>[text.toDiagnosticsNode(name: 'text', style: DiagnosticsTreeStyle.transition)];
|
return <DiagnosticsNode>[text.toDiagnosticsNode(name: 'text', style: DiagnosticsTreeStyle.transition)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
||||||
|
super.debugFillProperties(description);
|
||||||
|
description.add(new EnumProperty<TextAlign>('textAlign', textAlign));
|
||||||
|
description.add(new EnumProperty<TextDirection>('textDirection', textDirection));
|
||||||
|
description.add(new FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
|
||||||
|
description.add(new EnumProperty<TextOverflow>('overflow', overflow));
|
||||||
|
description.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: 1.0));
|
||||||
|
description.add(new IntProperty('maxLines', maxLines, ifNull: 'unlimited'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3114,17 +3114,21 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
|
|||||||
/// Creates a render object that attaches a semantic annotation.
|
/// Creates a render object that attaches a semantic annotation.
|
||||||
///
|
///
|
||||||
/// The [container] argument must not be null.
|
/// The [container] argument must not be null.
|
||||||
|
///
|
||||||
|
/// If the [label] is not null, the [textDirection] must also not be null.
|
||||||
RenderSemanticsAnnotations({
|
RenderSemanticsAnnotations({
|
||||||
RenderBox child,
|
RenderBox child,
|
||||||
bool container: false,
|
bool container: false,
|
||||||
bool checked,
|
bool checked,
|
||||||
bool selected,
|
bool selected,
|
||||||
String label,
|
String label,
|
||||||
|
TextDirection textDirection,
|
||||||
}) : assert(container != null),
|
}) : assert(container != null),
|
||||||
_container = container,
|
_container = container,
|
||||||
_checked = checked,
|
_checked = checked,
|
||||||
_selected = selected,
|
_selected = selected,
|
||||||
_label = label,
|
_label = label,
|
||||||
|
_textDirection = textDirection,
|
||||||
super(child);
|
super(child);
|
||||||
|
|
||||||
/// If 'container' is true, this RenderObject will introduce a new
|
/// If 'container' is true, this RenderObject will introduce a new
|
||||||
@ -3182,11 +3186,24 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
|
|||||||
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue);
|
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If non-null, sets the [SemanticsNode.textDirection] semantic to the given value.
|
||||||
|
///
|
||||||
|
/// This must not be null if [label] is not null.
|
||||||
|
TextDirection get textDirection => _textDirection;
|
||||||
|
TextDirection _textDirection;
|
||||||
|
set textDirection(TextDirection value) {
|
||||||
|
if (textDirection == value)
|
||||||
|
return;
|
||||||
|
final bool hadValue = textDirection != null;
|
||||||
|
_textDirection = value;
|
||||||
|
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get isSemanticBoundary => container;
|
bool get isSemanticBoundary => container;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SemanticsAnnotator get semanticsAnnotator => checked != null || selected != null || label != null ? _annotate : null;
|
SemanticsAnnotator get semanticsAnnotator => checked != null || selected != null || label != null || textDirection != null ? _annotate : null;
|
||||||
|
|
||||||
void _annotate(SemanticsNode node) {
|
void _annotate(SemanticsNode node) {
|
||||||
if (checked != null) {
|
if (checked != null) {
|
||||||
@ -3198,6 +3215,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
|
|||||||
node.isSelected = selected;
|
node.isSelected = selected;
|
||||||
if (label != null)
|
if (label != null)
|
||||||
node.label = label;
|
node.label = label;
|
||||||
|
if (textDirection != null)
|
||||||
|
node.textDirection = textDirection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,16 +92,20 @@ class SemanticsData {
|
|||||||
/// Creates a semantics data object.
|
/// Creates a semantics data object.
|
||||||
///
|
///
|
||||||
/// The [flags], [actions], [label], and [Rect] arguments must not be null.
|
/// The [flags], [actions], [label], and [Rect] arguments must not be null.
|
||||||
|
///
|
||||||
|
/// If [label] is not empty, then [textDirection] must also not be null.
|
||||||
const SemanticsData({
|
const SemanticsData({
|
||||||
@required this.flags,
|
@required this.flags,
|
||||||
@required this.actions,
|
@required this.actions,
|
||||||
@required this.label,
|
@required this.label,
|
||||||
|
@required this.textDirection,
|
||||||
@required this.rect,
|
@required this.rect,
|
||||||
@required this.tags,
|
@required this.tags,
|
||||||
this.transform
|
this.transform,
|
||||||
}) : assert(flags != null),
|
}) : assert(flags != null),
|
||||||
assert(actions != null),
|
assert(actions != null),
|
||||||
assert(label != null),
|
assert(label != null),
|
||||||
|
assert(label == '' || textDirection != null, 'A SemanticsData object with label "$label" had a null textDirection.'),
|
||||||
assert(rect != null),
|
assert(rect != null),
|
||||||
assert(tags != null);
|
assert(tags != null);
|
||||||
|
|
||||||
@ -112,8 +116,13 @@ class SemanticsData {
|
|||||||
final int actions;
|
final int actions;
|
||||||
|
|
||||||
/// A textual description of this node.
|
/// A textual description of this node.
|
||||||
|
///
|
||||||
|
/// The text's reading direction is given by [textDirection].
|
||||||
final String label;
|
final String label;
|
||||||
|
|
||||||
|
/// The reading direction for the text in [label].
|
||||||
|
final TextDirection textDirection;
|
||||||
|
|
||||||
/// The bounding box for this node in its coordinate system.
|
/// The bounding box for this node in its coordinate system.
|
||||||
final Rect rect;
|
final Rect rect;
|
||||||
|
|
||||||
@ -149,6 +158,8 @@ class SemanticsData {
|
|||||||
}
|
}
|
||||||
if (label.isNotEmpty)
|
if (label.isNotEmpty)
|
||||||
buffer.write('; "$label"');
|
buffer.write('; "$label"');
|
||||||
|
if (textDirection != null)
|
||||||
|
buffer.write('; $textDirection');
|
||||||
buffer.write(')');
|
buffer.write(')');
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
@ -161,13 +172,14 @@ class SemanticsData {
|
|||||||
return typedOther.flags == flags
|
return typedOther.flags == flags
|
||||||
&& typedOther.actions == actions
|
&& typedOther.actions == actions
|
||||||
&& typedOther.label == label
|
&& typedOther.label == label
|
||||||
|
&& typedOther.textDirection == textDirection
|
||||||
&& typedOther.rect == rect
|
&& typedOther.rect == rect
|
||||||
&& setEquals(typedOther.tags, tags)
|
&& setEquals(typedOther.tags, tags)
|
||||||
&& typedOther.transform == transform;
|
&& typedOther.transform == transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => hashValues(flags, actions, label, rect, tags, transform);
|
int get hashCode => hashValues(flags, actions, label, textDirection, rect, tags, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A node that represents some semantic data.
|
/// A node that represents some semantic data.
|
||||||
@ -342,6 +354,8 @@ class SemanticsNode extends AbstractNode {
|
|||||||
set isSelected(bool value) => _setFlag(SemanticsFlags.isSelected, value);
|
set isSelected(bool value) => _setFlag(SemanticsFlags.isSelected, value);
|
||||||
|
|
||||||
/// A textual description of this node.
|
/// A textual description of this node.
|
||||||
|
///
|
||||||
|
/// The text's reading direction is given by [textDirection].
|
||||||
String get label => _label;
|
String get label => _label;
|
||||||
String _label = '';
|
String _label = '';
|
||||||
set label(String value) {
|
set label(String value) {
|
||||||
@ -352,6 +366,17 @@ class SemanticsNode extends AbstractNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The reading direction for the text in [label].
|
||||||
|
TextDirection get textDirection => _textDirection;
|
||||||
|
TextDirection _textDirection;
|
||||||
|
set textDirection(TextDirection value) {
|
||||||
|
assert(value != null);
|
||||||
|
if (_textDirection != value) {
|
||||||
|
_textDirection = value;
|
||||||
|
_markDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final Set<SemanticsTag> _tags = new Set<SemanticsTag>();
|
final Set<SemanticsTag> _tags = new Set<SemanticsTag>();
|
||||||
|
|
||||||
/// Tags the [SemanticsNode] with [tag].
|
/// Tags the [SemanticsNode] with [tag].
|
||||||
@ -385,6 +410,7 @@ class SemanticsNode extends AbstractNode {
|
|||||||
if (hadInheritedMergeAllDescendantsIntoThisNode)
|
if (hadInheritedMergeAllDescendantsIntoThisNode)
|
||||||
_inheritedMergeAllDescendantsIntoThisNodeValue = true;
|
_inheritedMergeAllDescendantsIntoThisNodeValue = true;
|
||||||
_label = '';
|
_label = '';
|
||||||
|
_textDirection = null;
|
||||||
_tags.clear();
|
_tags.clear();
|
||||||
_markDirty();
|
_markDirty();
|
||||||
}
|
}
|
||||||
@ -598,18 +624,31 @@ class SemanticsNode extends AbstractNode {
|
|||||||
int flags = _flags;
|
int flags = _flags;
|
||||||
int actions = _actions;
|
int actions = _actions;
|
||||||
String label = _label;
|
String label = _label;
|
||||||
|
TextDirection textDirection = _textDirection;
|
||||||
final Set<SemanticsTag> tags = new Set<SemanticsTag>.from(_tags);
|
final Set<SemanticsTag> tags = new Set<SemanticsTag>.from(_tags);
|
||||||
|
|
||||||
if (mergeAllDescendantsIntoThisNode) {
|
if (mergeAllDescendantsIntoThisNode) {
|
||||||
_visitDescendants((SemanticsNode node) {
|
_visitDescendants((SemanticsNode node) {
|
||||||
flags |= node._flags;
|
flags |= node._flags;
|
||||||
actions |= node._actions;
|
actions |= node._actions;
|
||||||
|
textDirection ??= node._textDirection;
|
||||||
tags.addAll(node._tags);
|
tags.addAll(node._tags);
|
||||||
if (node.label.isNotEmpty) {
|
if (node.label.isNotEmpty) {
|
||||||
|
String nestedLabel = node.label;
|
||||||
|
if (textDirection != node.textDirection && node.textDirection != null) {
|
||||||
|
switch (node.textDirection) {
|
||||||
|
case TextDirection.rtl:
|
||||||
|
nestedLabel = '${Unicode.RLE}$nestedLabel${Unicode.PDF}';
|
||||||
|
break;
|
||||||
|
case TextDirection.ltr:
|
||||||
|
nestedLabel = '${Unicode.LRE}$nestedLabel${Unicode.PDF}';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (label.isEmpty)
|
if (label.isEmpty)
|
||||||
label = node.label;
|
label = nestedLabel;
|
||||||
else
|
else
|
||||||
label = '$label\n${node.label}';
|
label = '$label\n$nestedLabel';
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -619,6 +658,7 @@ class SemanticsNode extends AbstractNode {
|
|||||||
flags: flags,
|
flags: flags,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
label: label,
|
label: label,
|
||||||
|
textDirection: textDirection,
|
||||||
rect: rect,
|
rect: rect,
|
||||||
transform: transform,
|
transform: transform,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
@ -650,6 +690,7 @@ class SemanticsNode extends AbstractNode {
|
|||||||
actions: data.actions,
|
actions: data.actions,
|
||||||
rect: data.rect,
|
rect: data.rect,
|
||||||
label: data.label,
|
label: data.label,
|
||||||
|
textDirection: data.textDirection,
|
||||||
transform: data.transform?.storage ?? _kIdentityTransform,
|
transform: data.transform?.storage ?? _kIdentityTransform,
|
||||||
children: children,
|
children: children,
|
||||||
);
|
);
|
||||||
@ -696,6 +737,8 @@ class SemanticsNode extends AbstractNode {
|
|||||||
buffer.write('; selected');
|
buffer.write('; selected');
|
||||||
if (label.isNotEmpty)
|
if (label.isNotEmpty)
|
||||||
buffer.write('; "$label"');
|
buffer.write('; "$label"');
|
||||||
|
if (textDirection != null)
|
||||||
|
buffer.write('; $textDirection');
|
||||||
buffer.write(')');
|
buffer.write(')');
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
@ -553,7 +553,8 @@ class RenderSliverGrid extends RenderSliverMultiBoxAdaptor {
|
|||||||
for (int index = indexOf(firstChild) - 1; index >= firstIndex; --index) {
|
for (int index = indexOf(firstChild) - 1; index >= firstIndex; --index) {
|
||||||
final SliverGridGeometry gridGeometry = layout.getGeometryForChildIndex(index);
|
final SliverGridGeometry gridGeometry = layout.getGeometryForChildIndex(index);
|
||||||
final RenderBox child = insertAndLayoutLeadingChild(
|
final RenderBox child = insertAndLayoutLeadingChild(
|
||||||
gridGeometry.getBoxConstraints(constraints));
|
gridGeometry.getBoxConstraints(constraints),
|
||||||
|
);
|
||||||
final SliverGridParentData childParentData = child.parentData;
|
final SliverGridParentData childParentData = child.parentData;
|
||||||
childParentData.layoutOffset = gridGeometry.scrollOffset;
|
childParentData.layoutOffset = gridGeometry.scrollOffset;
|
||||||
childParentData.crossAxisOffset = gridGeometry.crossAxisOffset;
|
childParentData.crossAxisOffset = gridGeometry.crossAxisOffset;
|
||||||
|
@ -41,13 +41,15 @@ enum BannerLocation {
|
|||||||
class BannerPainter extends CustomPainter {
|
class BannerPainter extends CustomPainter {
|
||||||
/// Creates a banner painter.
|
/// Creates a banner painter.
|
||||||
///
|
///
|
||||||
/// The [message] and [location] arguments must not be null.
|
/// The [message], [textDirection], and [location] arguments must not be null.
|
||||||
BannerPainter({
|
BannerPainter({
|
||||||
@required this.message,
|
@required this.message,
|
||||||
|
@required this.textDirection,
|
||||||
@required this.location,
|
@required this.location,
|
||||||
this.color: _kColor,
|
this.color: _kColor,
|
||||||
this.textStyle: _kTextStyle,
|
this.textStyle: _kTextStyle,
|
||||||
}) : assert(message != null),
|
}) : assert(message != null),
|
||||||
|
assert(textDirection != null),
|
||||||
assert(location != null),
|
assert(location != null),
|
||||||
assert(color != null),
|
assert(color != null),
|
||||||
assert(textStyle != null);
|
assert(textStyle != null);
|
||||||
@ -55,6 +57,16 @@ class BannerPainter extends CustomPainter {
|
|||||||
/// The message to show in the banner.
|
/// The message to show in the banner.
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
|
/// The directionality of the text.
|
||||||
|
///
|
||||||
|
/// This is used to disambiguate how to render bidirectional text. For
|
||||||
|
/// example, if the message is an English phrase followed by a Hebrew phrase,
|
||||||
|
/// in a [TextDirection.ltr] context the English phrase will be on the left
|
||||||
|
/// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
|
||||||
|
/// context, the English phrase will be on the right and the Hebrow phrase on
|
||||||
|
/// its left.
|
||||||
|
final TextDirection textDirection;
|
||||||
|
|
||||||
/// Where to show the banner (e.g., the upper right corder).
|
/// Where to show the banner (e.g., the upper right corder).
|
||||||
final BannerLocation location;
|
final BannerLocation location;
|
||||||
|
|
||||||
@ -82,6 +94,7 @@ class BannerPainter extends CustomPainter {
|
|||||||
_textPainter = new TextPainter(
|
_textPainter = new TextPainter(
|
||||||
text: new TextSpan(style: textStyle, text: message),
|
text: new TextSpan(style: textStyle, text: message),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
textDirection: textDirection,
|
||||||
);
|
);
|
||||||
_prepared = true;
|
_prepared = true;
|
||||||
}
|
}
|
||||||
@ -169,6 +182,7 @@ class Banner extends StatelessWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
this.child,
|
this.child,
|
||||||
@required this.message,
|
@required this.message,
|
||||||
|
this.textDirection,
|
||||||
@required this.location,
|
@required this.location,
|
||||||
this.color: _kColor,
|
this.color: _kColor,
|
||||||
this.textStyle: _kTextStyle,
|
this.textStyle: _kTextStyle,
|
||||||
@ -184,6 +198,18 @@ class Banner extends StatelessWidget {
|
|||||||
/// The message to show in the banner.
|
/// The message to show in the banner.
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
|
/// The directionality of the text.
|
||||||
|
///
|
||||||
|
/// This is used to disambiguate how to render bidirectional text. For
|
||||||
|
/// example, if the message is an English phrase followed by a Hebrew phrase,
|
||||||
|
/// in a [TextDirection.ltr] context the English phrase will be on the left
|
||||||
|
/// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
|
||||||
|
/// context, the English phrase will be on the right and the Hebrow phrase on
|
||||||
|
/// its left.
|
||||||
|
///
|
||||||
|
/// Defaults to the ambient [Directionality], if any.
|
||||||
|
final TextDirection textDirection;
|
||||||
|
|
||||||
/// Where to show the banner (e.g., the upper right corder).
|
/// Where to show the banner (e.g., the upper right corder).
|
||||||
final BannerLocation location;
|
final BannerLocation location;
|
||||||
|
|
||||||
@ -198,6 +224,7 @@ class Banner extends StatelessWidget {
|
|||||||
return new CustomPaint(
|
return new CustomPaint(
|
||||||
foregroundPainter: new BannerPainter(
|
foregroundPainter: new BannerPainter(
|
||||||
message: message,
|
message: message,
|
||||||
|
textDirection: textDirection ?? Directionality.of(context),
|
||||||
location: location,
|
location: location,
|
||||||
color: color,
|
color: color,
|
||||||
textStyle: textStyle,
|
textStyle: textStyle,
|
||||||
@ -210,6 +237,7 @@ class Banner extends StatelessWidget {
|
|||||||
void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
||||||
super.debugFillProperties(description);
|
super.debugFillProperties(description);
|
||||||
description.add(new StringProperty('message', message, showName: false));
|
description.add(new StringProperty('message', message, showName: false));
|
||||||
|
description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
|
||||||
description.add(new EnumProperty<BannerLocation>('location', location));
|
description.add(new EnumProperty<BannerLocation>('location', location));
|
||||||
description.add(new DiagnosticsProperty<Color>('color', color, showName: false));
|
description.add(new DiagnosticsProperty<Color>('color', color, showName: false));
|
||||||
textStyle?.debugFillProperties(description, prefix: 'text ');
|
textStyle?.debugFillProperties(description, prefix: 'text ');
|
||||||
@ -236,7 +264,9 @@ class CheckedModeBanner extends StatelessWidget {
|
|||||||
result = new Banner(
|
result = new Banner(
|
||||||
child: result,
|
child: result,
|
||||||
message: 'SLOW MODE',
|
message: 'SLOW MODE',
|
||||||
location: BannerLocation.topRight);
|
textDirection: TextDirection.ltr,
|
||||||
|
location: BannerLocation.topRight,
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
|
@ -3704,20 +3704,25 @@ class Flow extends MultiChildRenderObjectWidget {
|
|||||||
class RichText extends LeafRenderObjectWidget {
|
class RichText extends LeafRenderObjectWidget {
|
||||||
/// Creates a paragraph of rich text.
|
/// Creates a paragraph of rich text.
|
||||||
///
|
///
|
||||||
/// The [text], [softWrap], [overflow], nad [textScaleFactor] arguments must
|
/// The [text], [textAlign], [softWrap], [overflow], nad [textScaleFactor]
|
||||||
/// not be null.
|
/// arguments must not be null.
|
||||||
///
|
///
|
||||||
/// The [maxLines] property may be null (and indeed defaults to null), but if
|
/// The [maxLines] property may be null (and indeed defaults to null), but if
|
||||||
/// it is not null, it must be greater than zero.
|
/// it is not null, it must be greater than zero.
|
||||||
|
///
|
||||||
|
/// The [textDirection], if null, defaults to the ambient [Directionality],
|
||||||
|
/// which in that case must not be null.
|
||||||
const RichText({
|
const RichText({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.text,
|
@required this.text,
|
||||||
this.textAlign,
|
this.textAlign: TextAlign.start,
|
||||||
|
this.textDirection,
|
||||||
this.softWrap: true,
|
this.softWrap: true,
|
||||||
this.overflow: TextOverflow.clip,
|
this.overflow: TextOverflow.clip,
|
||||||
this.textScaleFactor: 1.0,
|
this.textScaleFactor: 1.0,
|
||||||
this.maxLines,
|
this.maxLines,
|
||||||
}) : assert(text != null),
|
}) : assert(text != null),
|
||||||
|
assert(textAlign != null),
|
||||||
assert(softWrap != null),
|
assert(softWrap != null),
|
||||||
assert(overflow != null),
|
assert(overflow != null),
|
||||||
assert(textScaleFactor != null),
|
assert(textScaleFactor != null),
|
||||||
@ -3730,6 +3735,22 @@ class RichText extends LeafRenderObjectWidget {
|
|||||||
/// How the text should be aligned horizontally.
|
/// How the text should be aligned horizontally.
|
||||||
final TextAlign textAlign;
|
final TextAlign textAlign;
|
||||||
|
|
||||||
|
/// The directionality of the text.
|
||||||
|
///
|
||||||
|
/// This decides how [textAlign] values like [TextAlign.start] and
|
||||||
|
/// [TextAlign.end] are interpreted.
|
||||||
|
///
|
||||||
|
/// This is also used to disambiguate how to render bidirectional text. For
|
||||||
|
/// example, if the [text] is an English phrase followed by a Hebrew phrase,
|
||||||
|
/// in a [TextDirection.ltr] context the English phrase will be on the left
|
||||||
|
/// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
|
||||||
|
/// context, the English phrase will be on the right and the Hebrow phrase on
|
||||||
|
/// its left.
|
||||||
|
///
|
||||||
|
/// Defaults to the ambient [Directionality], if any. If there is no ambient
|
||||||
|
/// [Directionality], then this must not be null.
|
||||||
|
final TextDirection textDirection;
|
||||||
|
|
||||||
/// Whether the text should break at soft line breaks.
|
/// Whether the text should break at soft line breaks.
|
||||||
///
|
///
|
||||||
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
||||||
@ -3754,8 +3775,11 @@ class RichText extends LeafRenderObjectWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
RenderParagraph createRenderObject(BuildContext context) {
|
RenderParagraph createRenderObject(BuildContext context) {
|
||||||
|
final TextDirection direction = textDirection ?? Directionality.of(context);
|
||||||
|
assert(direction != null, 'A RichText was created with no textDirection and no ambient Directionality widget.');
|
||||||
return new RenderParagraph(text,
|
return new RenderParagraph(text,
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
|
textDirection: direction,
|
||||||
softWrap: softWrap,
|
softWrap: softWrap,
|
||||||
overflow: overflow,
|
overflow: overflow,
|
||||||
textScaleFactor: textScaleFactor,
|
textScaleFactor: textScaleFactor,
|
||||||
@ -3768,6 +3792,7 @@ class RichText extends LeafRenderObjectWidget {
|
|||||||
renderObject
|
renderObject
|
||||||
..text = text
|
..text = text
|
||||||
..textAlign = textAlign
|
..textAlign = textAlign
|
||||||
|
..textDirection = textDirection ?? Directionality.of(context)
|
||||||
..softWrap = softWrap
|
..softWrap = softWrap
|
||||||
..overflow = overflow
|
..overflow = overflow
|
||||||
..textScaleFactor = textScaleFactor
|
..textScaleFactor = textScaleFactor
|
||||||
@ -4289,6 +4314,7 @@ class Semantics extends SingleChildRenderObjectWidget {
|
|||||||
this.checked,
|
this.checked,
|
||||||
this.selected,
|
this.selected,
|
||||||
this.label,
|
this.label,
|
||||||
|
this.textDirection,
|
||||||
}) : assert(container != null),
|
}) : assert(container != null),
|
||||||
super(key: key, child: child);
|
super(key: key, child: child);
|
||||||
|
|
||||||
@ -4317,8 +4343,20 @@ class Semantics extends SingleChildRenderObjectWidget {
|
|||||||
final bool selected;
|
final bool selected;
|
||||||
|
|
||||||
/// Provides a textual description of the widget.
|
/// Provides a textual description of the widget.
|
||||||
|
///
|
||||||
|
/// If a label is provided, there must either by an ambient [Directionality]
|
||||||
|
/// or an explicit [textDirection] should be provided.
|
||||||
final String label;
|
final String label;
|
||||||
|
|
||||||
|
/// The reading direction of the [label].
|
||||||
|
///
|
||||||
|
/// Defaults to the ambient [Directionality].
|
||||||
|
final TextDirection textDirection;
|
||||||
|
|
||||||
|
TextDirection _getTextDirection(BuildContext context) {
|
||||||
|
return textDirection ?? (label != null ? Directionality.of(context) : null);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RenderSemanticsAnnotations createRenderObject(BuildContext context) {
|
RenderSemanticsAnnotations createRenderObject(BuildContext context) {
|
||||||
return new RenderSemanticsAnnotations(
|
return new RenderSemanticsAnnotations(
|
||||||
@ -4326,6 +4364,7 @@ class Semantics extends SingleChildRenderObjectWidget {
|
|||||||
checked: checked,
|
checked: checked,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
label: label,
|
label: label,
|
||||||
|
textDirection: _getTextDirection(context),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4335,7 +4374,8 @@ class Semantics extends SingleChildRenderObjectWidget {
|
|||||||
..container = container
|
..container = container
|
||||||
..checked = checked
|
..checked = checked
|
||||||
..selected = selected
|
..selected = selected
|
||||||
..label = label;
|
..label = label
|
||||||
|
..textDirection = _getTextDirection(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -4344,7 +4384,8 @@ class Semantics extends SingleChildRenderObjectWidget {
|
|||||||
description.add(new DiagnosticsProperty<bool>('container', container));
|
description.add(new DiagnosticsProperty<bool>('container', container));
|
||||||
description.add(new DiagnosticsProperty<bool>('checked', checked, defaultValue: null));
|
description.add(new DiagnosticsProperty<bool>('checked', checked, defaultValue: null));
|
||||||
description.add(new DiagnosticsProperty<bool>('selected', selected, defaultValue: null));
|
description.add(new DiagnosticsProperty<bool>('selected', selected, defaultValue: null));
|
||||||
description.add(new StringProperty('label', label));
|
description.add(new StringProperty('label', label, defaultValue: ''));
|
||||||
|
description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,8 +144,8 @@ class EditableText extends StatefulWidget {
|
|||||||
/// the number of lines. By default, it is 1, meaning this is a single-line
|
/// the number of lines. By default, it is 1, meaning this is a single-line
|
||||||
/// text field. If it is not null, it must be greater than zero.
|
/// text field. If it is not null, it must be greater than zero.
|
||||||
///
|
///
|
||||||
/// The [controller], [focusNode], [style], and [cursorColor] arguments must
|
/// The [controller], [focusNode], [style], [cursorColor], and [textAlign]
|
||||||
/// not be null.
|
/// arguments must not be null.
|
||||||
EditableText({
|
EditableText({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.controller,
|
@required this.controller,
|
||||||
@ -154,7 +154,8 @@ class EditableText extends StatefulWidget {
|
|||||||
this.autocorrect: true,
|
this.autocorrect: true,
|
||||||
@required this.style,
|
@required this.style,
|
||||||
@required this.cursorColor,
|
@required this.cursorColor,
|
||||||
this.textAlign,
|
this.textAlign: TextAlign.start,
|
||||||
|
this.textDirection,
|
||||||
this.textScaleFactor,
|
this.textScaleFactor,
|
||||||
this.maxLines: 1,
|
this.maxLines: 1,
|
||||||
this.autofocus: false,
|
this.autofocus: false,
|
||||||
@ -171,6 +172,7 @@ class EditableText extends StatefulWidget {
|
|||||||
assert(autocorrect != null),
|
assert(autocorrect != null),
|
||||||
assert(style != null),
|
assert(style != null),
|
||||||
assert(cursorColor != null),
|
assert(cursorColor != null),
|
||||||
|
assert(textAlign != null),
|
||||||
assert(maxLines == null || maxLines > 0),
|
assert(maxLines == null || maxLines > 0),
|
||||||
assert(autofocus != null),
|
assert(autofocus != null),
|
||||||
inputFormatters = maxLines == 1
|
inputFormatters = maxLines == 1
|
||||||
@ -201,8 +203,25 @@ class EditableText extends StatefulWidget {
|
|||||||
final TextStyle style;
|
final TextStyle style;
|
||||||
|
|
||||||
/// How the text should be aligned horizontally.
|
/// How the text should be aligned horizontally.
|
||||||
|
///
|
||||||
|
/// Defaults to [TextAlign.start].
|
||||||
final TextAlign textAlign;
|
final TextAlign textAlign;
|
||||||
|
|
||||||
|
/// The directionality of the text.
|
||||||
|
///
|
||||||
|
/// This decides how [textAlign] values like [TextAlign.start] and
|
||||||
|
/// [TextAlign.end] are interpreted.
|
||||||
|
///
|
||||||
|
/// This is also used to disambiguate how to render bidirectional text. For
|
||||||
|
/// example, if the text is an English phrase followed by a Hebrew phrase,
|
||||||
|
/// in a [TextDirection.ltr] context the English phrase will be on the left
|
||||||
|
/// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
|
||||||
|
/// context, the English phrase will be on the right and the Hebrow phrase on
|
||||||
|
/// its left.
|
||||||
|
///
|
||||||
|
/// Defaults to the ambient [Directionality], if any.
|
||||||
|
final TextDirection textDirection;
|
||||||
|
|
||||||
/// The number of font pixels for each logical pixel.
|
/// The number of font pixels for each logical pixel.
|
||||||
///
|
///
|
||||||
/// For example, if the text scale factor is 1.5, text will be 50% larger than
|
/// For example, if the text scale factor is 1.5, text will be 50% larger than
|
||||||
@ -266,6 +285,7 @@ class EditableText extends StatefulWidget {
|
|||||||
description.add(new DiagnosticsProperty<bool>('autocorrect', autocorrect, defaultValue: true));
|
description.add(new DiagnosticsProperty<bool>('autocorrect', autocorrect, defaultValue: true));
|
||||||
style?.debugFillProperties(description);
|
style?.debugFillProperties(description);
|
||||||
description.add(new EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
|
description.add(new EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
|
||||||
|
description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
|
||||||
description.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null));
|
description.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null));
|
||||||
description.add(new IntProperty('maxLines', maxLines, defaultValue: 1));
|
description.add(new IntProperty('maxLines', maxLines, defaultValue: 1));
|
||||||
description.add(new DiagnosticsProperty<bool>('autofocus', autofocus, defaultValue: false));
|
description.add(new DiagnosticsProperty<bool>('autofocus', autofocus, defaultValue: false));
|
||||||
@ -575,6 +595,12 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||||||
updateKeepAlive();
|
updateKeepAlive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextDirection get _textDirection {
|
||||||
|
final TextDirection result = widget.textDirection ?? Directionality.of(context);
|
||||||
|
assert(result != null, '$runtimeType created without a textDirection and with no ambient Directionality.');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
FocusScope.of(context).reparentIfNeeded(widget.focusNode);
|
FocusScope.of(context).reparentIfNeeded(widget.focusNode);
|
||||||
@ -595,6 +621,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||||||
selectionColor: widget.selectionColor,
|
selectionColor: widget.selectionColor,
|
||||||
textScaleFactor: widget.textScaleFactor ?? MediaQuery.of(context, nullOk: true)?.textScaleFactor ?? 1.0,
|
textScaleFactor: widget.textScaleFactor ?? MediaQuery.of(context, nullOk: true)?.textScaleFactor ?? 1.0,
|
||||||
textAlign: widget.textAlign,
|
textAlign: widget.textAlign,
|
||||||
|
textDirection: _textDirection,
|
||||||
obscureText: widget.obscureText,
|
obscureText: widget.obscureText,
|
||||||
obscureShowCharacterAtIndex: _obscureShowCharTicksPending > 0 ? _obscureLatestCharIndex : null,
|
obscureShowCharacterAtIndex: _obscureShowCharTicksPending > 0 ? _obscureLatestCharIndex : null,
|
||||||
autocorrect: widget.autocorrect,
|
autocorrect: widget.autocorrect,
|
||||||
@ -619,13 +646,15 @@ class _Editable extends LeafRenderObjectWidget {
|
|||||||
this.selectionColor,
|
this.selectionColor,
|
||||||
this.textScaleFactor,
|
this.textScaleFactor,
|
||||||
this.textAlign,
|
this.textAlign,
|
||||||
|
@required this.textDirection,
|
||||||
this.obscureText,
|
this.obscureText,
|
||||||
this.obscureShowCharacterAtIndex,
|
this.obscureShowCharacterAtIndex,
|
||||||
this.autocorrect,
|
this.autocorrect,
|
||||||
this.offset,
|
this.offset,
|
||||||
this.onSelectionChanged,
|
this.onSelectionChanged,
|
||||||
this.onCaretChanged,
|
this.onCaretChanged,
|
||||||
}) : super(key: key);
|
}) : assert(textDirection != null),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
final TextEditingValue value;
|
final TextEditingValue value;
|
||||||
final TextStyle style;
|
final TextStyle style;
|
||||||
@ -635,6 +664,7 @@ class _Editable extends LeafRenderObjectWidget {
|
|||||||
final Color selectionColor;
|
final Color selectionColor;
|
||||||
final double textScaleFactor;
|
final double textScaleFactor;
|
||||||
final TextAlign textAlign;
|
final TextAlign textAlign;
|
||||||
|
final TextDirection textDirection;
|
||||||
final bool obscureText;
|
final bool obscureText;
|
||||||
final int obscureShowCharacterAtIndex;
|
final int obscureShowCharacterAtIndex;
|
||||||
final bool autocorrect;
|
final bool autocorrect;
|
||||||
@ -652,6 +682,7 @@ class _Editable extends LeafRenderObjectWidget {
|
|||||||
selectionColor: selectionColor,
|
selectionColor: selectionColor,
|
||||||
textScaleFactor: textScaleFactor,
|
textScaleFactor: textScaleFactor,
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
|
textDirection: textDirection,
|
||||||
selection: value.selection,
|
selection: value.selection,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
onSelectionChanged: onSelectionChanged,
|
onSelectionChanged: onSelectionChanged,
|
||||||
@ -669,6 +700,7 @@ class _Editable extends LeafRenderObjectWidget {
|
|||||||
..selectionColor = selectionColor
|
..selectionColor = selectionColor
|
||||||
..textScaleFactor = textScaleFactor
|
..textScaleFactor = textScaleFactor
|
||||||
..textAlign = textAlign
|
..textAlign = textAlign
|
||||||
|
..textDirection = textDirection
|
||||||
..selection = value.selection
|
..selection = value.selection
|
||||||
..offset = offset
|
..offset = offset
|
||||||
..onSelectionChanged = onSelectionChanged
|
..onSelectionChanged = onSelectionChanged
|
||||||
|
@ -17,6 +17,10 @@ import 'icon_theme_data.dart';
|
|||||||
/// Icons are not interactive. For an interactive icon, consider material's
|
/// Icons are not interactive. For an interactive icon, consider material's
|
||||||
/// [IconButton].
|
/// [IconButton].
|
||||||
///
|
///
|
||||||
|
/// There must be an ambient [Directionality] widget when using [Icon].
|
||||||
|
/// Typically this is introduced automatically by the [WidgetsApp] or
|
||||||
|
/// [MaterialApp].
|
||||||
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [IconButton], for interactive icons.
|
/// * [IconButton], for interactive icons.
|
||||||
@ -80,6 +84,9 @@ class Icon extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final TextDirection textDirection = Directionality.of(context);
|
||||||
|
assert(textDirection != null, 'Icon widgets required an ambient Directionality.');
|
||||||
|
|
||||||
final IconThemeData iconTheme = IconTheme.of(context);
|
final IconThemeData iconTheme = IconTheme.of(context);
|
||||||
|
|
||||||
final double iconSize = size ?? iconTheme.size;
|
final double iconSize = size ?? iconTheme.size;
|
||||||
@ -98,18 +105,19 @@ class Icon extends StatelessWidget {
|
|||||||
height: iconSize,
|
height: iconSize,
|
||||||
child: new Center(
|
child: new Center(
|
||||||
child: new RichText(
|
child: new RichText(
|
||||||
|
textDirection: textDirection, // Since we already fetched it for the assert...
|
||||||
text: new TextSpan(
|
text: new TextSpan(
|
||||||
text: new String.fromCharCode(icon.codePoint),
|
text: new String.fromCharCode(icon.codePoint),
|
||||||
style: new TextStyle(
|
style: new TextStyle(
|
||||||
inherit: false,
|
inherit: false,
|
||||||
color: iconColor,
|
color: iconColor,
|
||||||
fontSize: iconSize,
|
fontSize: iconSize,
|
||||||
fontFamily: icon.fontFamily
|
fontFamily: icon.fontFamily,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,15 +228,29 @@ String _getMessage(SemanticsNode node) {
|
|||||||
if (isAdjustable)
|
if (isAdjustable)
|
||||||
annotations.add('adjustable');
|
annotations.add('adjustable');
|
||||||
|
|
||||||
String message;
|
|
||||||
if (annotations.isEmpty) {
|
|
||||||
assert(data.label != null);
|
assert(data.label != null);
|
||||||
message = data.label;
|
String message;
|
||||||
} else {
|
|
||||||
if (data.label.isEmpty) {
|
if (data.label.isEmpty) {
|
||||||
message = annotations.join('; ');
|
message = annotations.join('; ');
|
||||||
} else {
|
} else {
|
||||||
message = '${data.label} (${annotations.join('; ')})';
|
String label;
|
||||||
|
if (data.textDirection == null) {
|
||||||
|
label = '${Unicode.FSI}${data.label}${Unicode.PDI}';
|
||||||
|
annotations.insert(0, 'MISSING TEXT DIRECTION');
|
||||||
|
} else {
|
||||||
|
switch (data.textDirection) {
|
||||||
|
case TextDirection.rtl:
|
||||||
|
label = '${Unicode.RLI}${data.label}${Unicode.PDF}';
|
||||||
|
break;
|
||||||
|
case TextDirection.ltr:
|
||||||
|
label = data.label;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (annotations.isEmpty) {
|
||||||
|
message = label;
|
||||||
|
} else {
|
||||||
|
message = '$label (${annotations.join('; ')})';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +271,11 @@ void _paintMessage(Canvas canvas, SemanticsNode node) {
|
|||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.clipRect(rect);
|
canvas.clipRect(rect);
|
||||||
final TextPainter textPainter = new TextPainter()
|
final TextPainter textPainter = new TextPainter()
|
||||||
..text = new TextSpan(style: _messageStyle, text: message)
|
..text = new TextSpan(
|
||||||
|
style: _messageStyle,
|
||||||
|
text: message,
|
||||||
|
)
|
||||||
|
..textDirection = TextDirection.ltr // _getMessage always returns LTR text, even if node.label is RTL
|
||||||
..textAlign = TextAlign.center
|
..textAlign = TextAlign.center
|
||||||
..layout(maxWidth: rect.width);
|
..layout(maxWidth: rect.width);
|
||||||
|
|
||||||
|
@ -200,6 +200,7 @@ class Text extends StatelessWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
this.style,
|
this.style,
|
||||||
this.textAlign,
|
this.textAlign,
|
||||||
|
this.textDirection,
|
||||||
this.softWrap,
|
this.softWrap,
|
||||||
this.overflow,
|
this.overflow,
|
||||||
this.textScaleFactor,
|
this.textScaleFactor,
|
||||||
@ -220,6 +221,21 @@ class Text extends StatelessWidget {
|
|||||||
/// How the text should be aligned horizontally.
|
/// How the text should be aligned horizontally.
|
||||||
final TextAlign textAlign;
|
final TextAlign textAlign;
|
||||||
|
|
||||||
|
/// The directionality of the text.
|
||||||
|
///
|
||||||
|
/// This decides how [textAlign] values like [TextAlign.start] and
|
||||||
|
/// [TextAlign.end] are interpreted.
|
||||||
|
///
|
||||||
|
/// This is also used to disambiguate how to render bidirectional text. For
|
||||||
|
/// example, if the [data] is an English phrase followed by a Hebrew phrase,
|
||||||
|
/// in a [TextDirection.ltr] context the English phrase will be on the left
|
||||||
|
/// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
|
||||||
|
/// context, the English phrase will be on the right and the Hebrow phrase on
|
||||||
|
/// its left.
|
||||||
|
///
|
||||||
|
/// Defaults to the ambient [Directionality], if any.
|
||||||
|
final TextDirection textDirection;
|
||||||
|
|
||||||
/// Whether the text should break at soft line breaks.
|
/// Whether the text should break at soft line breaks.
|
||||||
///
|
///
|
||||||
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
||||||
@ -257,7 +273,8 @@ class Text extends StatelessWidget {
|
|||||||
if (style == null || style.inherit)
|
if (style == null || style.inherit)
|
||||||
effectiveTextStyle = defaultTextStyle.style.merge(style);
|
effectiveTextStyle = defaultTextStyle.style.merge(style);
|
||||||
return new RichText(
|
return new RichText(
|
||||||
textAlign: textAlign ?? defaultTextStyle.textAlign,
|
textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start,
|
||||||
|
textDirection: textDirection, // RichText uses Directionality.of to obtain a default if this is null.
|
||||||
softWrap: softWrap ?? defaultTextStyle.softWrap,
|
softWrap: softWrap ?? defaultTextStyle.softWrap,
|
||||||
overflow: overflow ?? defaultTextStyle.overflow,
|
overflow: overflow ?? defaultTextStyle.overflow,
|
||||||
textScaleFactor: textScaleFactor ?? MediaQuery.of(context, nullOk: true)?.textScaleFactor ?? 1.0,
|
textScaleFactor: textScaleFactor ?? MediaQuery.of(context, nullOk: true)?.textScaleFactor ?? 1.0,
|
||||||
|
@ -361,12 +361,14 @@ class _InspectorOverlayRenderState {
|
|||||||
@required this.selected,
|
@required this.selected,
|
||||||
@required this.candidates,
|
@required this.candidates,
|
||||||
@required this.tooltip,
|
@required this.tooltip,
|
||||||
|
@required this.textDirection,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Rect overlayRect;
|
final Rect overlayRect;
|
||||||
final _TransformedRect selected;
|
final _TransformedRect selected;
|
||||||
final List<_TransformedRect> candidates;
|
final List<_TransformedRect> candidates;
|
||||||
final String tooltip;
|
final String tooltip;
|
||||||
|
final TextDirection textDirection;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(dynamic other) {
|
bool operator ==(dynamic other) {
|
||||||
@ -447,6 +449,7 @@ class _InspectorOverlayLayer extends Layer {
|
|||||||
overlayRect: overlayRect,
|
overlayRect: overlayRect,
|
||||||
selected: new _TransformedRect(selected),
|
selected: new _TransformedRect(selected),
|
||||||
tooltip: selected.toString(),
|
tooltip: selected.toString(),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
candidates: candidates,
|
candidates: candidates,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -497,15 +500,22 @@ class _InspectorOverlayLayer extends Layer {
|
|||||||
final double offsetFromWidget = 9.0;
|
final double offsetFromWidget = 9.0;
|
||||||
final double verticalOffset = (targetRect.height) / 2 + offsetFromWidget;
|
final double verticalOffset = (targetRect.height) / 2 + offsetFromWidget;
|
||||||
|
|
||||||
_paintDescription(canvas, state.tooltip, target, verticalOffset, size, targetRect);
|
_paintDescription(canvas, state.tooltip, state.textDirection, target, verticalOffset, size, targetRect);
|
||||||
|
|
||||||
// TODO(jacobr): provide an option to perform a debug paint of just the
|
// TODO(jacobr): provide an option to perform a debug paint of just the
|
||||||
// selected widget.
|
// selected widget.
|
||||||
return recorder.endRecording();
|
return recorder.endRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _paintDescription(Canvas canvas, String message, Offset target,
|
void _paintDescription(
|
||||||
double verticalOffset, Size size, Rect targetRect) {
|
Canvas canvas,
|
||||||
|
String message,
|
||||||
|
TextDirection textDirection,
|
||||||
|
Offset target,
|
||||||
|
double verticalOffset,
|
||||||
|
Size size,
|
||||||
|
Rect targetRect,
|
||||||
|
) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
final double maxWidth = size.width - 2 * (_kScreenEdgeMargin + _kTooltipPadding);
|
final double maxWidth = size.width - 2 * (_kScreenEdgeMargin + _kTooltipPadding);
|
||||||
if (_textPainter == null || _textPainter.text.text != message || _textPainterMaxWidth != maxWidth) {
|
if (_textPainter == null || _textPainter.text.text != message || _textPainterMaxWidth != maxWidth) {
|
||||||
@ -514,6 +524,7 @@ class _InspectorOverlayLayer extends Layer {
|
|||||||
..maxLines = _kMaxTooltipLines
|
..maxLines = _kMaxTooltipLines
|
||||||
..ellipsis = '...'
|
..ellipsis = '...'
|
||||||
..text = new TextSpan(style: _messageStyle, text: message)
|
..text = new TextSpan(style: _messageStyle, text: message)
|
||||||
|
..textDirection = textDirection
|
||||||
..layout(maxWidth: maxWidth);
|
..layout(maxWidth: maxWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ const TextStyle testStyle = const TextStyle(
|
|||||||
void main() {
|
void main() {
|
||||||
testWidgets('Default layout minimum size', (WidgetTester tester) async {
|
testWidgets('Default layout minimum size', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const Center(child: const CupertinoButton(
|
boilerplate(child: const CupertinoButton(
|
||||||
child: const Text('X', style: testStyle),
|
child: const Text('X', style: testStyle),
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
))
|
))
|
||||||
@ -30,7 +30,7 @@ void main() {
|
|||||||
testWidgets('Minimum size parameter', (WidgetTester tester) async {
|
testWidgets('Minimum size parameter', (WidgetTester tester) async {
|
||||||
final double minSize = 60.0;
|
final double minSize = 60.0;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Center(child: new CupertinoButton(
|
boilerplate(child: new CupertinoButton(
|
||||||
child: const Text('X', style: testStyle),
|
child: const Text('X', style: testStyle),
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
minSize: minSize,
|
minSize: minSize,
|
||||||
@ -46,7 +46,7 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Size grows with text', (WidgetTester tester) async {
|
testWidgets('Size grows with text', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const Center(child: const CupertinoButton(
|
boilerplate(child: const CupertinoButton(
|
||||||
child: const Text('XXXX', style: testStyle),
|
child: const Text('XXXX', style: testStyle),
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
))
|
))
|
||||||
@ -60,7 +60,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Button with background is wider', (WidgetTester tester) async {
|
testWidgets('Button with background is wider', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const Center(child: const CupertinoButton(
|
await tester.pumpWidget(boilerplate(child: const CupertinoButton(
|
||||||
child: const Text('X', style: testStyle),
|
child: const Text('X', style: testStyle),
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
color: const Color(0xFFFFFFFF),
|
color: const Color(0xFFFFFFFF),
|
||||||
@ -74,7 +74,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Custom padding', (WidgetTester tester) async {
|
testWidgets('Custom padding', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const Center(child: const CupertinoButton(
|
await tester.pumpWidget(boilerplate(child: const CupertinoButton(
|
||||||
child: const Text(' ', style: testStyle),
|
child: const Text(' ', style: testStyle),
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
padding: const EdgeInsets.all(100.0),
|
padding: const EdgeInsets.all(100.0),
|
||||||
@ -91,7 +91,7 @@ void main() {
|
|||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new StatefulBuilder(
|
new StatefulBuilder(
|
||||||
builder: (BuildContext context, StateSetter setState) {
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
return new Center(
|
return boilerplate(
|
||||||
child: new CupertinoButton(
|
child: new CupertinoButton(
|
||||||
child: const Text('Tap me'),
|
child: const Text('Tap me'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -115,7 +115,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Disabled button doesn\'t animate', (WidgetTester tester) async {
|
testWidgets('Disabled button doesn\'t animate', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const Center(child: const CupertinoButton(
|
await tester.pumpWidget(boilerplate(child: const CupertinoButton(
|
||||||
child: const Text('Tap me'),
|
child: const Text('Tap me'),
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
)));
|
)));
|
||||||
@ -126,7 +126,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('pressedOpacity defaults to 0.1', (WidgetTester tester) async {
|
testWidgets('pressedOpacity defaults to 0.1', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(new Center(child: new CupertinoButton(
|
await tester.pumpWidget(boilerplate(child: new CupertinoButton(
|
||||||
child: const Text('Tap me'),
|
child: const Text('Tap me'),
|
||||||
onPressed: () { },
|
onPressed: () { },
|
||||||
)));
|
)));
|
||||||
@ -146,7 +146,7 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('pressedOpacity parameter', (WidgetTester tester) async {
|
testWidgets('pressedOpacity parameter', (WidgetTester tester) async {
|
||||||
final double pressedOpacity = 0.5;
|
final double pressedOpacity = 0.5;
|
||||||
await tester.pumpWidget(new Center(child: new CupertinoButton(
|
await tester.pumpWidget(boilerplate(child: new CupertinoButton(
|
||||||
pressedOpacity: pressedOpacity,
|
pressedOpacity: pressedOpacity,
|
||||||
child: const Text('Tap me'),
|
child: const Text('Tap me'),
|
||||||
onPressed: () { },
|
onPressed: () { },
|
||||||
@ -165,3 +165,10 @@ void main() {
|
|||||||
expect(opacity.opacity, pressedOpacity);
|
expect(opacity.opacity, pressedOpacity);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget boilerplate({ Widget child }) {
|
||||||
|
return new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Center(child: child),
|
||||||
|
);
|
||||||
|
}
|
@ -64,10 +64,10 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Dialog destructive action styles', (WidgetTester tester) async {
|
testWidgets('Dialog destructive action styles', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const CupertinoDialogAction(
|
await tester.pumpWidget(boilerplate(const CupertinoDialogAction(
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
child: const Text('Ok'),
|
child: const Text('Ok'),
|
||||||
));
|
)));
|
||||||
|
|
||||||
final DefaultTextStyle widget = tester.widget(find.byType(DefaultTextStyle));
|
final DefaultTextStyle widget = tester.widget(find.byType(DefaultTextStyle));
|
||||||
|
|
||||||
@ -76,10 +76,10 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Dialog default action styles', (WidgetTester tester) async {
|
testWidgets('Dialog default action styles', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const CupertinoDialogAction(
|
await tester.pumpWidget(boilerplate(const CupertinoDialogAction(
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
child: const Text('Ok'),
|
child: const Text('Ok'),
|
||||||
));
|
)));
|
||||||
|
|
||||||
final DefaultTextStyle widget = tester.widget(find.byType(DefaultTextStyle));
|
final DefaultTextStyle widget = tester.widget(find.byType(DefaultTextStyle));
|
||||||
|
|
||||||
@ -87,11 +87,11 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Default and destructive style', (WidgetTester tester) async {
|
testWidgets('Default and destructive style', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const CupertinoDialogAction(
|
await tester.pumpWidget(boilerplate(const CupertinoDialogAction(
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
child: const Text('Ok'),
|
child: const Text('Ok'),
|
||||||
));
|
)));
|
||||||
|
|
||||||
final DefaultTextStyle widget = tester.widget(find.byType(DefaultTextStyle));
|
final DefaultTextStyle widget = tester.widget(find.byType(DefaultTextStyle));
|
||||||
|
|
||||||
@ -99,3 +99,10 @@ void main() {
|
|||||||
expect(widget.style.color.red, greaterThan(widget.style.color.blue));
|
expect(widget.style.color.red, greaterThan(widget.style.color.blue));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget boilerplate(Widget child) {
|
||||||
|
return new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -14,14 +14,17 @@ void main() {
|
|||||||
testWidgets('Does FlatButton contribute semantics', (WidgetTester tester) async {
|
testWidgets('Does FlatButton contribute semantics', (WidgetTester tester) async {
|
||||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Material(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Material(
|
||||||
child: new Center(
|
child: new Center(
|
||||||
child: new FlatButton(
|
child: new FlatButton(
|
||||||
onPressed: () { },
|
onPressed: () { },
|
||||||
child: const Text('ABC')
|
child: const Text('ABC')
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(semantics, hasSemantics(
|
expect(semantics, hasSemantics(
|
||||||
@ -58,10 +61,13 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Theme(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Theme(
|
||||||
data: new ThemeData(),
|
data: new ThemeData(),
|
||||||
child: buttonWidget,
|
child: buttonWidget,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final Offset center = tester.getCenter(find.byType(MaterialButton));
|
final Offset center = tester.getCenter(find.byType(MaterialButton));
|
||||||
@ -88,13 +94,16 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Theme(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Theme(
|
||||||
data: new ThemeData(
|
data: new ThemeData(
|
||||||
highlightColor: themeHighlightColor1,
|
highlightColor: themeHighlightColor1,
|
||||||
splashColor: themeSplashColor1,
|
splashColor: themeSplashColor1,
|
||||||
),
|
),
|
||||||
child: buttonWidget,
|
child: buttonWidget,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@ -108,13 +117,16 @@ void main() {
|
|||||||
final Color themeHighlightColor2 = const Color(0xFF002200);
|
final Color themeHighlightColor2 = const Color(0xFF002200);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Theme(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Theme(
|
||||||
data: new ThemeData(
|
data: new ThemeData(
|
||||||
highlightColor: themeHighlightColor2,
|
highlightColor: themeHighlightColor2,
|
||||||
splashColor: themeSplashColor2,
|
splashColor: themeSplashColor2,
|
||||||
),
|
),
|
||||||
child: buttonWidget, // same widget, so does not get updated because of us
|
child: buttonWidget, // same widget, so does not get updated because of us
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -10,7 +10,7 @@ void main() {
|
|||||||
testWidgets('CircleAvatar with background color', (WidgetTester tester) async {
|
testWidgets('CircleAvatar with background color', (WidgetTester tester) async {
|
||||||
final Color backgroundColor = Colors.blue.shade400;
|
final Color backgroundColor = Colors.blue.shade400;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Center(
|
wrap(
|
||||||
child: new CircleAvatar(
|
child: new CircleAvatar(
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
radius: 50.0,
|
radius: 50.0,
|
||||||
@ -33,7 +33,7 @@ void main() {
|
|||||||
testWidgets('CircleAvatar with foreground color', (WidgetTester tester) async {
|
testWidgets('CircleAvatar with foreground color', (WidgetTester tester) async {
|
||||||
final Color foregroundColor = Colors.red.shade100;
|
final Color foregroundColor = Colors.red.shade100;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Center(
|
wrap(
|
||||||
child: new CircleAvatar(
|
child: new CircleAvatar(
|
||||||
foregroundColor: foregroundColor,
|
foregroundColor: foregroundColor,
|
||||||
child: const Text('Z'),
|
child: const Text('Z'),
|
||||||
@ -60,9 +60,9 @@ void main() {
|
|||||||
primaryColorBrightness: Brightness.light,
|
primaryColorBrightness: Brightness.light,
|
||||||
);
|
);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Theme(
|
wrap(
|
||||||
|
child: new Theme(
|
||||||
data: theme,
|
data: theme,
|
||||||
child: const Center(
|
|
||||||
child: const CircleAvatar(
|
child: const CircleAvatar(
|
||||||
child: const Text('Z'),
|
child: const Text('Z'),
|
||||||
),
|
),
|
||||||
@ -79,3 +79,10 @@ void main() {
|
|||||||
expect(paragraph.text.style.color, equals(theme.primaryTextTheme.title.color));
|
expect(paragraph.text.style.color, equals(theme.primaryTextTheme.title.color));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget wrap({ Widget child }) {
|
||||||
|
return new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Center(child: child),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -10,15 +10,13 @@ void main() {
|
|||||||
bool expanded = false;
|
bool expanded = false;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Material(
|
wrap(
|
||||||
child: new Center(
|
|
||||||
child: new ExpandIcon(
|
child: new ExpandIcon(
|
||||||
onPressed: (bool isExpanded) {
|
onPressed: (bool isExpanded) {
|
||||||
expanded = !expanded;
|
expanded = !expanded;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(expanded, isFalse);
|
expect(expanded, isFalse);
|
||||||
@ -31,13 +29,11 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('ExpandIcon disabled', (WidgetTester tester) async {
|
testWidgets('ExpandIcon disabled', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const Material(
|
wrap(
|
||||||
child: const Center(
|
|
||||||
child: const ExpandIcon(
|
child: const ExpandIcon(
|
||||||
onPressed: null
|
onPressed: null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final IconTheme iconTheme = tester.firstWidget(find.byType(IconTheme));
|
final IconTheme iconTheme = tester.firstWidget(find.byType(IconTheme));
|
||||||
@ -48,8 +44,7 @@ void main() {
|
|||||||
bool expanded = false;
|
bool expanded = false;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Material(
|
wrap(
|
||||||
child: new Center(
|
|
||||||
child: new ExpandIcon(
|
child: new ExpandIcon(
|
||||||
isExpanded: false,
|
isExpanded: false,
|
||||||
onPressed: (bool isExpanded) {
|
onPressed: (bool isExpanded) {
|
||||||
@ -57,12 +52,10 @@ void main() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Material(
|
wrap(
|
||||||
child: new Center(
|
|
||||||
child: new ExpandIcon(
|
child: new ExpandIcon(
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
onPressed: (bool isExpanded) {
|
onPressed: (bool isExpanded) {
|
||||||
@ -70,9 +63,17 @@ void main() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(expanded, isFalse);
|
expect(expanded, isFalse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget wrap({ Widget child }) {
|
||||||
|
return new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Center(
|
||||||
|
child: new Material(child: child),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -154,7 +154,7 @@ class TestWidget extends StatelessWidget {
|
|||||||
return new GestureDetector(
|
return new GestureDetector(
|
||||||
onTap: tapHandler(context),
|
onTap: tapHandler(context),
|
||||||
onLongPress: longPressHandler(context),
|
onLongPress: longPressHandler(context),
|
||||||
child: const Text('X'),
|
child: const Text('X', textDirection: TextDirection.ltr),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,12 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Floating Action Button control test',
|
testWidgets('Floating Action Button control test', (WidgetTester tester) async {
|
||||||
(WidgetTester tester) async {
|
|
||||||
bool didPressButton = false;
|
bool didPressButton = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Center(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Center(
|
||||||
child: new FloatingActionButton(
|
child: new FloatingActionButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
didPressButton = true;
|
didPressButton = true;
|
||||||
@ -18,6 +19,7 @@ void main() {
|
|||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(didPressButton, isFalse);
|
expect(didPressButton, isFalse);
|
||||||
|
@ -38,7 +38,12 @@ void main() {
|
|||||||
expect(tester.getBottomLeft(find.byKey(headerKey)).dy,
|
expect(tester.getBottomLeft(find.byKey(headerKey)).dy,
|
||||||
lessThan(tester.getTopLeft(find.byKey(footerKey)).dy));
|
lessThan(tester.getTopLeft(find.byKey(footerKey)).dy));
|
||||||
|
|
||||||
await tester.pumpWidget(const GridTile(child: const Text('Simple')));
|
await tester.pumpWidget(
|
||||||
|
const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: const GridTile(child: const Text('Simple')),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
expect(find.text('Simple'), findsOneWidget);
|
expect(find.text('Simple'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
@ -24,14 +24,12 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('test default icon buttons are sized up to 48', (WidgetTester tester) async {
|
testWidgets('test default icon buttons are sized up to 48', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Material(
|
wrap(
|
||||||
child: new Center(
|
|
||||||
child: new IconButton(
|
child: new IconButton(
|
||||||
onPressed: mockOnPressedFunction,
|
onPressed: mockOnPressedFunction,
|
||||||
icon: const Icon(Icons.link),
|
icon: const Icon(Icons.link),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderBox iconButton = tester.renderObject(find.byType(IconButton));
|
final RenderBox iconButton = tester.renderObject(find.byType(IconButton));
|
||||||
@ -43,15 +41,13 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('test small icons are sized up to 48dp', (WidgetTester tester) async {
|
testWidgets('test small icons are sized up to 48dp', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Material(
|
wrap(
|
||||||
child: new Center(
|
|
||||||
child: new IconButton(
|
child: new IconButton(
|
||||||
iconSize: 10.0,
|
iconSize: 10.0,
|
||||||
onPressed: mockOnPressedFunction,
|
onPressed: mockOnPressedFunction,
|
||||||
icon: const Icon(Icons.link),
|
icon: const Icon(Icons.link),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderBox iconButton = tester.renderObject(find.byType(IconButton));
|
final RenderBox iconButton = tester.renderObject(find.byType(IconButton));
|
||||||
@ -60,8 +56,7 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('test icons can be small when total size is >48dp', (WidgetTester tester) async {
|
testWidgets('test icons can be small when total size is >48dp', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Material(
|
wrap(
|
||||||
child: new Center(
|
|
||||||
child: new IconButton(
|
child: new IconButton(
|
||||||
iconSize: 10.0,
|
iconSize: 10.0,
|
||||||
padding: const EdgeInsets.all(30.0),
|
padding: const EdgeInsets.all(30.0),
|
||||||
@ -69,7 +64,6 @@ void main() {
|
|||||||
icon: const Icon(Icons.link),
|
icon: const Icon(Icons.link),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderBox iconButton = tester.renderObject(find.byType(IconButton));
|
final RenderBox iconButton = tester.renderObject(find.byType(IconButton));
|
||||||
@ -78,8 +72,7 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('test default icon buttons are constrained', (WidgetTester tester) async {
|
testWidgets('test default icon buttons are constrained', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Material(
|
wrap(
|
||||||
child: new Center(
|
|
||||||
child: new IconButton(
|
child: new IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
onPressed: mockOnPressedFunction,
|
onPressed: mockOnPressedFunction,
|
||||||
@ -87,7 +80,6 @@ void main() {
|
|||||||
iconSize: 80.0,
|
iconSize: 80.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderBox box = tester.renderObject(find.byType(IconButton));
|
final RenderBox box = tester.renderObject(find.byType(IconButton));
|
||||||
@ -120,15 +112,13 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('test default padding', (WidgetTester tester) async {
|
testWidgets('test default padding', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Material(
|
wrap(
|
||||||
child: new Center(
|
|
||||||
child: new IconButton(
|
child: new IconButton(
|
||||||
onPressed: mockOnPressedFunction,
|
onPressed: mockOnPressedFunction,
|
||||||
icon: const Icon(Icons.ac_unit),
|
icon: const Icon(Icons.ac_unit),
|
||||||
iconSize: 80.0,
|
iconSize: 80.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderBox box = tester.renderObject(find.byType(IconButton));
|
final RenderBox box = tester.renderObject(find.byType(IconButton));
|
||||||
@ -203,15 +193,13 @@ void main() {
|
|||||||
final Color directSplashColor = const Color(0xFF00000F);
|
final Color directSplashColor = const Color(0xFF00000F);
|
||||||
final Color directHighlightColor = const Color(0xFF0000F0);
|
final Color directHighlightColor = const Color(0xFF0000F0);
|
||||||
|
|
||||||
Widget buttonWidget = new Material(
|
Widget buttonWidget = wrap(
|
||||||
child: new Center(
|
|
||||||
child: new IconButton(
|
child: new IconButton(
|
||||||
icon: const Icon(Icons.android),
|
icon: const Icon(Icons.android),
|
||||||
splashColor: directSplashColor,
|
splashColor: directSplashColor,
|
||||||
highlightColor: directHighlightColor,
|
highlightColor: directHighlightColor,
|
||||||
onPressed: () { /* enable the button */ },
|
onPressed: () { /* enable the button */ },
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -236,13 +224,11 @@ void main() {
|
|||||||
final Color themeSplashColor1 = const Color(0xFF000F00);
|
final Color themeSplashColor1 = const Color(0xFF000F00);
|
||||||
final Color themeHighlightColor1 = const Color(0xFF00FF00);
|
final Color themeHighlightColor1 = const Color(0xFF00FF00);
|
||||||
|
|
||||||
buttonWidget = new Material(
|
buttonWidget = wrap(
|
||||||
child: new Center(
|
|
||||||
child: new IconButton(
|
child: new IconButton(
|
||||||
icon: const Icon(Icons.android),
|
icon: const Icon(Icons.android),
|
||||||
onPressed: () { /* enable the button */ },
|
onPressed: () { /* enable the button */ },
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -285,3 +271,12 @@ void main() {
|
|||||||
await gesture.up();
|
await gesture.up();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget wrap({ Widget child }) {
|
||||||
|
return new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Material(
|
||||||
|
child: new Center(child: child),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -23,9 +23,8 @@ void main() {
|
|||||||
testWidgets('RefreshIndicator', (WidgetTester tester) async {
|
testWidgets('RefreshIndicator', (WidgetTester tester) async {
|
||||||
refreshCalled = false;
|
refreshCalled = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Directionality(
|
new MaterialApp(
|
||||||
textDirection: TextDirection.ltr,
|
home: new RefreshIndicator(
|
||||||
child: new RefreshIndicator(
|
|
||||||
onRefresh: refresh,
|
onRefresh: refresh,
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
@ -51,9 +50,8 @@ void main() {
|
|||||||
testWidgets('RefreshIndicator - bottom', (WidgetTester tester) async {
|
testWidgets('RefreshIndicator - bottom', (WidgetTester tester) async {
|
||||||
refreshCalled = false;
|
refreshCalled = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Directionality(
|
new MaterialApp(
|
||||||
textDirection: TextDirection.ltr,
|
home: new RefreshIndicator(
|
||||||
child: new RefreshIndicator(
|
|
||||||
onRefresh: refresh,
|
onRefresh: refresh,
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
reverse: true,
|
reverse: true,
|
||||||
@ -80,9 +78,8 @@ void main() {
|
|||||||
testWidgets('RefreshIndicator - top - position', (WidgetTester tester) async {
|
testWidgets('RefreshIndicator - top - position', (WidgetTester tester) async {
|
||||||
refreshCalled = false;
|
refreshCalled = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Directionality(
|
new MaterialApp(
|
||||||
textDirection: TextDirection.ltr,
|
home: new RefreshIndicator(
|
||||||
child: new RefreshIndicator(
|
|
||||||
onRefresh: holdRefresh,
|
onRefresh: holdRefresh,
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
@ -107,9 +104,8 @@ void main() {
|
|||||||
testWidgets('RefreshIndicator - bottom - position', (WidgetTester tester) async {
|
testWidgets('RefreshIndicator - bottom - position', (WidgetTester tester) async {
|
||||||
refreshCalled = false;
|
refreshCalled = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Directionality(
|
new MaterialApp(
|
||||||
textDirection: TextDirection.ltr,
|
home: new RefreshIndicator(
|
||||||
child: new RefreshIndicator(
|
|
||||||
onRefresh: holdRefresh,
|
onRefresh: holdRefresh,
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
reverse: true,
|
reverse: true,
|
||||||
@ -135,9 +131,8 @@ void main() {
|
|||||||
testWidgets('RefreshIndicator - no movement', (WidgetTester tester) async {
|
testWidgets('RefreshIndicator - no movement', (WidgetTester tester) async {
|
||||||
refreshCalled = false;
|
refreshCalled = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Directionality(
|
new MaterialApp(
|
||||||
textDirection: TextDirection.ltr,
|
home: new RefreshIndicator(
|
||||||
child: new RefreshIndicator(
|
|
||||||
onRefresh: refresh,
|
onRefresh: refresh,
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
@ -164,9 +159,8 @@ void main() {
|
|||||||
testWidgets('RefreshIndicator - not enough', (WidgetTester tester) async {
|
testWidgets('RefreshIndicator - not enough', (WidgetTester tester) async {
|
||||||
refreshCalled = false;
|
refreshCalled = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Directionality(
|
new MaterialApp(
|
||||||
textDirection: TextDirection.ltr,
|
home: new RefreshIndicator(
|
||||||
child: new RefreshIndicator(
|
|
||||||
onRefresh: refresh,
|
onRefresh: refresh,
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
@ -192,9 +186,8 @@ void main() {
|
|||||||
testWidgets('RefreshIndicator - show - slow', (WidgetTester tester) async {
|
testWidgets('RefreshIndicator - show - slow', (WidgetTester tester) async {
|
||||||
refreshCalled = false;
|
refreshCalled = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Directionality(
|
new MaterialApp(
|
||||||
textDirection: TextDirection.ltr,
|
home: new RefreshIndicator(
|
||||||
child: new RefreshIndicator(
|
|
||||||
onRefresh: holdRefresh, // this one never returns
|
onRefresh: holdRefresh, // this one never returns
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
@ -236,9 +229,8 @@ void main() {
|
|||||||
testWidgets('RefreshIndicator - show - fast', (WidgetTester tester) async {
|
testWidgets('RefreshIndicator - show - fast', (WidgetTester tester) async {
|
||||||
refreshCalled = false;
|
refreshCalled = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Directionality(
|
new MaterialApp(
|
||||||
textDirection: TextDirection.ltr,
|
home: new RefreshIndicator(
|
||||||
child: new RefreshIndicator(
|
|
||||||
onRefresh: refresh,
|
onRefresh: refresh,
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
@ -281,9 +273,8 @@ void main() {
|
|||||||
testWidgets('RefreshIndicator - show - fast - twice', (WidgetTester tester) async {
|
testWidgets('RefreshIndicator - show - fast - twice', (WidgetTester tester) async {
|
||||||
refreshCalled = false;
|
refreshCalled = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Directionality(
|
new MaterialApp(
|
||||||
textDirection: TextDirection.ltr,
|
home: new RefreshIndicator(
|
||||||
child: new RefreshIndicator(
|
|
||||||
onRefresh: refresh,
|
onRefresh: refresh,
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
@ -1260,7 +1260,7 @@ void main() {
|
|||||||
testWidgets('Cannot enter new lines onto single line TextField', (WidgetTester tester) async {
|
testWidgets('Cannot enter new lines onto single line TextField', (WidgetTester tester) async {
|
||||||
final TextEditingController textController = new TextEditingController();
|
final TextEditingController textController = new TextEditingController();
|
||||||
|
|
||||||
await tester.pumpWidget(new Material(
|
await tester.pumpWidget(boilerplate(
|
||||||
child: new TextField(controller: textController, decoration: null),
|
child: new TextField(controller: textController, decoration: null),
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -1272,7 +1272,7 @@ void main() {
|
|||||||
testWidgets('Injected formatters are chained', (WidgetTester tester) async {
|
testWidgets('Injected formatters are chained', (WidgetTester tester) async {
|
||||||
final TextEditingController textController = new TextEditingController();
|
final TextEditingController textController = new TextEditingController();
|
||||||
|
|
||||||
await tester.pumpWidget(new Material(
|
await tester.pumpWidget(boilerplate(
|
||||||
child: new TextField(
|
child: new TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
decoration: null,
|
decoration: null,
|
||||||
@ -1293,7 +1293,7 @@ void main() {
|
|||||||
testWidgets('Chained formatters are in sequence', (WidgetTester tester) async {
|
testWidgets('Chained formatters are in sequence', (WidgetTester tester) async {
|
||||||
final TextEditingController textController = new TextEditingController();
|
final TextEditingController textController = new TextEditingController();
|
||||||
|
|
||||||
await tester.pumpWidget(new Material(
|
await tester.pumpWidget(boilerplate(
|
||||||
child: new TextField(
|
child: new TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
decoration: null,
|
decoration: null,
|
||||||
|
@ -32,7 +32,9 @@ void main() {
|
|||||||
testWidgets('Does tooltip end up in the right place - center', (WidgetTester tester) async {
|
testWidgets('Does tooltip end up in the right place - center', (WidgetTester tester) async {
|
||||||
final GlobalKey key = new GlobalKey();
|
final GlobalKey key = new GlobalKey();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Overlay(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Overlay(
|
||||||
initialEntries: <OverlayEntry>[
|
initialEntries: <OverlayEntry>[
|
||||||
new OverlayEntry(
|
new OverlayEntry(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@ -50,16 +52,17 @@ void main() {
|
|||||||
preferBelow: false,
|
preferBelow: false,
|
||||||
child: new Container(
|
child: new Container(
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
height: 0.0
|
height: 0.0,
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
]
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
||||||
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
||||||
@ -85,7 +88,9 @@ void main() {
|
|||||||
testWidgets('Does tooltip end up in the right place - top left', (WidgetTester tester) async {
|
testWidgets('Does tooltip end up in the right place - top left', (WidgetTester tester) async {
|
||||||
final GlobalKey key = new GlobalKey();
|
final GlobalKey key = new GlobalKey();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Overlay(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Overlay(
|
||||||
initialEntries: <OverlayEntry>[
|
initialEntries: <OverlayEntry>[
|
||||||
new OverlayEntry(
|
new OverlayEntry(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@ -103,16 +108,17 @@ void main() {
|
|||||||
preferBelow: false,
|
preferBelow: false,
|
||||||
child: new Container(
|
child: new Container(
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
height: 0.0
|
height: 0.0,
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
]
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
||||||
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
||||||
@ -134,7 +140,9 @@ void main() {
|
|||||||
testWidgets('Does tooltip end up in the right place - center prefer above fits', (WidgetTester tester) async {
|
testWidgets('Does tooltip end up in the right place - center prefer above fits', (WidgetTester tester) async {
|
||||||
final GlobalKey key = new GlobalKey();
|
final GlobalKey key = new GlobalKey();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Overlay(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Overlay(
|
||||||
initialEntries: <OverlayEntry>[
|
initialEntries: <OverlayEntry>[
|
||||||
new OverlayEntry(
|
new OverlayEntry(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@ -152,16 +160,17 @@ void main() {
|
|||||||
preferBelow: false,
|
preferBelow: false,
|
||||||
child: new Container(
|
child: new Container(
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
height: 0.0
|
height: 0.0,
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
]
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
||||||
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
||||||
@ -185,7 +194,9 @@ void main() {
|
|||||||
testWidgets('Does tooltip end up in the right place - center prefer above does not fit', (WidgetTester tester) async {
|
testWidgets('Does tooltip end up in the right place - center prefer above does not fit', (WidgetTester tester) async {
|
||||||
final GlobalKey key = new GlobalKey();
|
final GlobalKey key = new GlobalKey();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Overlay(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Overlay(
|
||||||
initialEntries: <OverlayEntry>[
|
initialEntries: <OverlayEntry>[
|
||||||
new OverlayEntry(
|
new OverlayEntry(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@ -203,16 +214,17 @@ void main() {
|
|||||||
preferBelow: false,
|
preferBelow: false,
|
||||||
child: new Container(
|
child: new Container(
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
height: 0.0
|
height: 0.0,
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
]
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
||||||
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
||||||
@ -247,7 +259,9 @@ void main() {
|
|||||||
testWidgets('Does tooltip end up in the right place - center prefer below fits', (WidgetTester tester) async {
|
testWidgets('Does tooltip end up in the right place - center prefer below fits', (WidgetTester tester) async {
|
||||||
final GlobalKey key = new GlobalKey();
|
final GlobalKey key = new GlobalKey();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Overlay(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Overlay(
|
||||||
initialEntries: <OverlayEntry>[
|
initialEntries: <OverlayEntry>[
|
||||||
new OverlayEntry(
|
new OverlayEntry(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@ -265,16 +279,17 @@ void main() {
|
|||||||
preferBelow: true,
|
preferBelow: true,
|
||||||
child: new Container(
|
child: new Container(
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
height: 0.0
|
height: 0.0,
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
]
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
||||||
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
||||||
@ -297,7 +312,9 @@ void main() {
|
|||||||
testWidgets('Does tooltip end up in the right place - way off to the right', (WidgetTester tester) async {
|
testWidgets('Does tooltip end up in the right place - way off to the right', (WidgetTester tester) async {
|
||||||
final GlobalKey key = new GlobalKey();
|
final GlobalKey key = new GlobalKey();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Overlay(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Overlay(
|
||||||
initialEntries: <OverlayEntry>[
|
initialEntries: <OverlayEntry>[
|
||||||
new OverlayEntry(
|
new OverlayEntry(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@ -315,16 +332,17 @@ void main() {
|
|||||||
preferBelow: true,
|
preferBelow: true,
|
||||||
child: new Container(
|
child: new Container(
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
height: 0.0
|
height: 0.0,
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
]
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
||||||
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
||||||
@ -349,7 +367,9 @@ void main() {
|
|||||||
testWidgets('Does tooltip end up in the right place - near the edge', (WidgetTester tester) async {
|
testWidgets('Does tooltip end up in the right place - near the edge', (WidgetTester tester) async {
|
||||||
final GlobalKey key = new GlobalKey();
|
final GlobalKey key = new GlobalKey();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Overlay(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Overlay(
|
||||||
initialEntries: <OverlayEntry>[
|
initialEntries: <OverlayEntry>[
|
||||||
new OverlayEntry(
|
new OverlayEntry(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@ -367,16 +387,17 @@ void main() {
|
|||||||
preferBelow: true,
|
preferBelow: true,
|
||||||
child: new Container(
|
child: new Container(
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
height: 0.0
|
height: 0.0,
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
]
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
(key.currentState as dynamic).ensureTooltipVisible(); // before using "as dynamic" in your code, see note top of file
|
||||||
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
|
||||||
@ -438,7 +459,9 @@ void main() {
|
|||||||
|
|
||||||
final GlobalKey key = new GlobalKey();
|
final GlobalKey key = new GlobalKey();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Overlay(
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Overlay(
|
||||||
initialEntries: <OverlayEntry>[
|
initialEntries: <OverlayEntry>[
|
||||||
new OverlayEntry(
|
new OverlayEntry(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@ -450,15 +473,16 @@ void main() {
|
|||||||
child: new Tooltip(
|
child: new Tooltip(
|
||||||
key: key,
|
key: key,
|
||||||
message: tooltipText,
|
message: tooltipText,
|
||||||
child: new Container(width: 0.0, height: 0.0)
|
child: new Container(width: 0.0, height: 0.0),
|
||||||
)
|
|
||||||
),
|
),
|
||||||
]
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(semantics, hasSemantics(new TestSemantics.root(label: tooltipText)));
|
expect(semantics, hasSemantics(new TestSemantics.root(label: tooltipText)));
|
||||||
@ -504,7 +528,8 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Haptic feedback', (WidgetTester tester) async {
|
testWidgets('Haptic feedback', (WidgetTester tester) async {
|
||||||
final FeedbackTester feedback = new FeedbackTester();
|
final FeedbackTester feedback = new FeedbackTester();
|
||||||
await tester.pumpWidget(new MaterialApp(
|
await tester.pumpWidget(
|
||||||
|
new MaterialApp(
|
||||||
home: new Center(
|
home: new Center(
|
||||||
child: new Tooltip(
|
child: new Tooltip(
|
||||||
message: 'Foo',
|
message: 'Foo',
|
||||||
@ -512,10 +537,10 @@ void main() {
|
|||||||
width: 100.0,
|
width: 100.0,
|
||||||
height: 100.0,
|
height: 100.0,
|
||||||
color: Colors.green[500],
|
color: Colors.green[500],
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.longPress(find.byType(Tooltip));
|
await tester.longPress(find.byType(Tooltip));
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:io' as io;
|
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:flutter/painting.dart';
|
import 'package:flutter/painting.dart';
|
||||||
@ -10,7 +9,8 @@ import 'package:flutter_test/flutter_test.dart';
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('TextPainter caret test', () {
|
test('TextPainter caret test', () {
|
||||||
final TextPainter painter = new TextPainter();
|
final TextPainter painter = new TextPainter()
|
||||||
|
..textDirection = TextDirection.ltr;
|
||||||
|
|
||||||
String text = 'A';
|
String text = 'A';
|
||||||
painter.text = new TextSpan(text: text);
|
painter.text = new TextSpan(text: text);
|
||||||
@ -27,13 +27,20 @@ void main() {
|
|||||||
painter.layout();
|
painter.layout();
|
||||||
caretOffset = painter.getOffsetForCaret(new ui.TextPosition(offset: text.length), ui.Rect.zero);
|
caretOffset = painter.getOffsetForCaret(new ui.TextPosition(offset: text.length), ui.Rect.zero);
|
||||||
expect(caretOffset.dx, painter.width);
|
expect(caretOffset.dx, painter.width);
|
||||||
}, skip: io.Platform.isMacOS); // TODO(goderbauer): Disabled because of https://github.com/flutter/flutter/issues/4273
|
});
|
||||||
|
|
||||||
test('TextPainter error test', () {
|
test('TextPainter error test', () {
|
||||||
final TextPainter painter = new TextPainter();
|
final TextPainter painter = new TextPainter(textDirection: TextDirection.ltr);
|
||||||
expect(() { painter.paint(null, Offset.zero); }, throwsFlutterError);
|
expect(() { painter.paint(null, Offset.zero); }, throwsFlutterError);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('TextPainter requires textDirection', () {
|
||||||
|
final TextPainter painter1 = new TextPainter(text: const TextSpan(text: ''));
|
||||||
|
expect(() { painter1.layout(); }, throwsAssertionError);
|
||||||
|
final TextPainter painter2 = new TextPainter(text: const TextSpan(text: ''), textDirection: TextDirection.rtl);
|
||||||
|
expect(() { painter2.layout(); }, isNot(throwsException));
|
||||||
|
});
|
||||||
|
|
||||||
test('TextPainter size test', () {
|
test('TextPainter size test', () {
|
||||||
final TextPainter painter = new TextPainter(
|
final TextPainter painter = new TextPainter(
|
||||||
text: const TextSpan(
|
text: const TextSpan(
|
||||||
@ -44,20 +51,27 @@ void main() {
|
|||||||
fontSize: 123.0,
|
fontSize: 123.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
painter.layout();
|
painter.layout();
|
||||||
expect(painter.size, const Size(123.0, 123.0));
|
expect(painter.size, const Size(123.0, 123.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('TextPainter default text height is 14 pixels', () {
|
test('TextPainter default text height is 14 pixels', () {
|
||||||
final TextPainter painter = new TextPainter(text: const TextSpan(text: 'x'));
|
final TextPainter painter = new TextPainter(
|
||||||
|
text: const TextSpan(text: 'x'),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
);
|
||||||
painter.layout();
|
painter.layout();
|
||||||
expect(painter.preferredLineHeight, 14.0);
|
expect(painter.preferredLineHeight, 14.0);
|
||||||
expect(painter.size, const Size(14.0, 14.0));
|
expect(painter.size, const Size(14.0, 14.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('TextPainter sets paragraph size from root', () {
|
test('TextPainter sets paragraph size from root', () {
|
||||||
final TextPainter painter = new TextPainter(text: const TextSpan(text: 'x', style: const TextStyle(fontSize: 100.0)));
|
final TextPainter painter = new TextPainter(
|
||||||
|
text: const TextSpan(text: 'x', style: const TextStyle(fontSize: 100.0)),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
);
|
||||||
painter.layout();
|
painter.layout();
|
||||||
expect(painter.preferredLineHeight, 100.0);
|
expect(painter.preferredLineHeight, 100.0);
|
||||||
expect(painter.size, const Size(100.0, 100.0));
|
expect(painter.size, const Size(100.0, 100.0));
|
||||||
|
@ -8,7 +8,7 @@ import 'package:flutter/painting.dart';
|
|||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test("TextStyle control test", () {
|
test('TextStyle control test', () {
|
||||||
expect(
|
expect(
|
||||||
const TextStyle(inherit: false).toString(),
|
const TextStyle(inherit: false).toString(),
|
||||||
equals('TextStyle(inherit: false, <no style specified>)'),
|
equals('TextStyle(inherit: false, <no style specified>)'),
|
||||||
@ -117,9 +117,17 @@ void main() {
|
|||||||
|
|
||||||
final ui.ParagraphStyle ps2 = s2.getParagraphStyle(textAlign: TextAlign.center);
|
final ui.ParagraphStyle ps2 = s2.getParagraphStyle(textAlign: TextAlign.center);
|
||||||
expect(ps2, equals(new ui.ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontSize: 10.0, lineHeight: 100.0)));
|
expect(ps2, equals(new ui.ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontSize: 10.0, lineHeight: 100.0)));
|
||||||
expect(ps2.toString(), 'ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 10.0, lineHeight: 100.0x, ellipsis: unspecified)');
|
expect(ps2.toString(), 'ParagraphStyle(textAlign: TextAlign.center, textDirection: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 10.0, lineHeight: 100.0x, ellipsis: unspecified)');
|
||||||
final ui.ParagraphStyle ps5 = s5.getParagraphStyle();
|
final ui.ParagraphStyle ps5 = s5.getParagraphStyle();
|
||||||
expect(ps5, equals(new ui.ParagraphStyle(fontWeight: FontWeight.w700, fontSize: 12.0, lineHeight: 123.0)));
|
expect(ps5, equals(new ui.ParagraphStyle(fontWeight: FontWeight.w700, fontSize: 12.0, lineHeight: 123.0)));
|
||||||
expect(ps5.toString(), 'ParagraphStyle(textAlign: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 12.0, lineHeight: 123.0x, ellipsis: unspecified)');
|
expect(ps5.toString(), 'ParagraphStyle(textAlign: unspecified, textDirection: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 12.0, lineHeight: 123.0x, ellipsis: unspecified)');
|
||||||
|
|
||||||
|
final ui.ParagraphStyle ps6 = const TextStyle().getParagraphStyle(textDirection: TextDirection.ltr);
|
||||||
|
expect(ps6, equals(new ui.ParagraphStyle(textDirection: TextDirection.ltr)));
|
||||||
|
expect(ps6.toString(), 'ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.ltr, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: unspecified, lineHeight: unspecified, ellipsis: unspecified)');
|
||||||
|
|
||||||
|
final ui.ParagraphStyle ps7 = const TextStyle().getParagraphStyle(textDirection: TextDirection.rtl);
|
||||||
|
expect(ps7, equals(new ui.ParagraphStyle(textDirection: TextDirection.rtl)));
|
||||||
|
expect(ps7.toString(), 'ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.rtl, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: unspecified, lineHeight: unspecified, ellipsis: unspecified)');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -73,8 +73,8 @@ void main() {
|
|||||||
expect(coloredBox, hasAGoodToStringDeep);
|
expect(coloredBox, hasAGoodToStringDeep);
|
||||||
expect(coloredBox.toStringDeep(), equalsIgnoringHashCodes(
|
expect(coloredBox.toStringDeep(), equalsIgnoringHashCodes(
|
||||||
'RenderDecoratedBox#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
'RenderDecoratedBox#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
||||||
' parentData: null\n'
|
' parentData: MISSING\n'
|
||||||
' constraints: null\n'
|
' constraints: MISSING\n'
|
||||||
' size: MISSING\n'
|
' size: MISSING\n'
|
||||||
' decoration: BoxDecoration:\n'
|
' decoration: BoxDecoration:\n'
|
||||||
' <no decorations specified>\n'
|
' <no decorations specified>\n'
|
||||||
|
@ -12,6 +12,8 @@ void main() {
|
|||||||
style: const TextStyle(height: 1.0, fontSize: 10.0, fontFamily: 'Ahem'),
|
style: const TextStyle(height: 1.0, fontSize: 10.0, fontFamily: 'Ahem'),
|
||||||
text: '12345',
|
text: '12345',
|
||||||
),
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
offset: new ViewportOffset.zero(),
|
offset: new ViewportOffset.zero(),
|
||||||
);
|
);
|
||||||
expect(editable.getMinIntrinsicWidth(double.INFINITY), 50.0);
|
expect(editable.getMinIntrinsicWidth(double.INFINITY), 50.0);
|
||||||
@ -23,8 +25,8 @@ void main() {
|
|||||||
editable.toStringDeep(),
|
editable.toStringDeep(),
|
||||||
equalsIgnoringHashCodes(
|
equalsIgnoringHashCodes(
|
||||||
'RenderEditable#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
'RenderEditable#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
||||||
' │ parentData: null\n'
|
' │ parentData: MISSING\n'
|
||||||
' │ constraints: null\n'
|
' │ constraints: MISSING\n'
|
||||||
' │ size: MISSING\n'
|
' │ size: MISSING\n'
|
||||||
' │ cursorColor: null\n'
|
' │ cursorColor: null\n'
|
||||||
' │ showCursor: ValueNotifier<bool>#00000(false)\n'
|
' │ showCursor: ValueNotifier<bool>#00000(false)\n'
|
||||||
|
@ -91,8 +91,8 @@ void main() {
|
|||||||
flex.toStringDeep(),
|
flex.toStringDeep(),
|
||||||
equalsIgnoringHashCodes(
|
equalsIgnoringHashCodes(
|
||||||
'RenderFlex#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
'RenderFlex#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
||||||
' parentData: null\n'
|
' parentData: MISSING\n'
|
||||||
' constraints: null\n'
|
' constraints: MISSING\n'
|
||||||
' size: MISSING\n'
|
' size: MISSING\n'
|
||||||
' direction: horizontal\n'
|
' direction: horizontal\n'
|
||||||
' mainAxisAlignment: start\n'
|
' mainAxisAlignment: start\n'
|
||||||
|
@ -9,13 +9,16 @@ import 'package:test/test.dart';
|
|||||||
import 'rendering_tester.dart';
|
import 'rendering_tester.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test("overflow should not affect baseline", () {
|
test('overflow should not affect baseline', () {
|
||||||
RenderBox root, child, text;
|
RenderBox root, child, text;
|
||||||
double baseline1, baseline2, height1, height2;
|
double baseline1, baseline2, height1, height2;
|
||||||
|
|
||||||
root = new RenderPositionedBox(
|
root = new RenderPositionedBox(
|
||||||
child: new RenderCustomPaint(
|
child: new RenderCustomPaint(
|
||||||
child: child = text = new RenderParagraph(const TextSpan(text: 'Hello World')),
|
child: child = text = new RenderParagraph(
|
||||||
|
const TextSpan(text: 'Hello World'),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
),
|
||||||
painter: new TestCallbackPainter(
|
painter: new TestCallbackPainter(
|
||||||
onPaint: () {
|
onPaint: () {
|
||||||
baseline1 = child.getDistanceToBaseline(TextBaseline.alphabetic);
|
baseline1 = child.getDistanceToBaseline(TextBaseline.alphabetic);
|
||||||
@ -29,7 +32,10 @@ void main() {
|
|||||||
root = new RenderPositionedBox(
|
root = new RenderPositionedBox(
|
||||||
child: new RenderCustomPaint(
|
child: new RenderCustomPaint(
|
||||||
child: child = new RenderConstrainedOverflowBox(
|
child: child = new RenderConstrainedOverflowBox(
|
||||||
child: text = new RenderParagraph(const TextSpan(text: 'Hello World')),
|
child: text = new RenderParagraph(
|
||||||
|
const TextSpan(text: 'Hello World'),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
),
|
||||||
maxHeight: height1 / 2.0,
|
maxHeight: height1 / 2.0,
|
||||||
alignment: const FractionalOffset(0.0, 0.0)
|
alignment: const FractionalOffset(0.0, 0.0)
|
||||||
),
|
),
|
||||||
|
@ -10,8 +10,9 @@ void main() {
|
|||||||
final RenderParagraph paragraph = new RenderParagraph(
|
final RenderParagraph paragraph = new RenderParagraph(
|
||||||
const TextSpan(
|
const TextSpan(
|
||||||
style: const TextStyle(height: 1.0),
|
style: const TextStyle(height: 1.0),
|
||||||
text: 'Hello World'
|
text: 'Hello World',
|
||||||
)
|
),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
final RenderListBody testBlock = new RenderListBody(
|
final RenderListBody testBlock = new RenderListBody(
|
||||||
children: <RenderBox>[
|
children: <RenderBox>[
|
||||||
|
@ -14,7 +14,10 @@ const String _kText = 'I polished up that handle so carefullee\nThat now I am th
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('getOffsetForCaret control test', () {
|
test('getOffsetForCaret control test', () {
|
||||||
final RenderParagraph paragraph = new RenderParagraph(const TextSpan(text: _kText));
|
final RenderParagraph paragraph = new RenderParagraph(
|
||||||
|
const TextSpan(text: _kText),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
);
|
||||||
layout(paragraph);
|
layout(paragraph);
|
||||||
|
|
||||||
final Rect caret = new Rect.fromLTWH(0.0, 0.0, 2.0, 20.0);
|
final Rect caret = new Rect.fromLTWH(0.0, 0.0, 2.0, 20.0);
|
||||||
@ -30,7 +33,10 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('getPositionForOffset control test', () {
|
test('getPositionForOffset control test', () {
|
||||||
final RenderParagraph paragraph = new RenderParagraph(const TextSpan(text: _kText));
|
final RenderParagraph paragraph = new RenderParagraph(
|
||||||
|
const TextSpan(text: _kText),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
);
|
||||||
layout(paragraph);
|
layout(paragraph);
|
||||||
|
|
||||||
final TextPosition position20 = paragraph.getPositionForOffset(const Offset(20.0, 5.0));
|
final TextPosition position20 = paragraph.getPositionForOffset(const Offset(20.0, 5.0));
|
||||||
@ -44,7 +50,10 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('getBoxesForSelection control test', () {
|
test('getBoxesForSelection control test', () {
|
||||||
final RenderParagraph paragraph = new RenderParagraph(const TextSpan(text: _kText));
|
final RenderParagraph paragraph = new RenderParagraph(
|
||||||
|
const TextSpan(text: _kText),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
);
|
||||||
layout(paragraph);
|
layout(paragraph);
|
||||||
|
|
||||||
List<ui.TextBox> boxes = paragraph.getBoxesForSelection(
|
List<ui.TextBox> boxes = paragraph.getBoxesForSelection(
|
||||||
@ -61,7 +70,10 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('getWordBoundary control test', () {
|
test('getWordBoundary control test', () {
|
||||||
final RenderParagraph paragraph = new RenderParagraph(const TextSpan(text: _kText));
|
final RenderParagraph paragraph = new RenderParagraph(
|
||||||
|
const TextSpan(text: _kText),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
);
|
||||||
layout(paragraph);
|
layout(paragraph);
|
||||||
|
|
||||||
final TextRange range5 = paragraph.getWordBoundary(const TextPosition(offset: 5));
|
final TextRange range5 = paragraph.getWordBoundary(const TextPosition(offset: 5));
|
||||||
@ -81,6 +93,7 @@ void main() {
|
|||||||
'is a wrapping test. It should wrap at manual newlines, and if softWrap is true, also at spaces.',
|
'is a wrapping test. It should wrap at manual newlines, and if softWrap is true, also at spaces.',
|
||||||
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
|
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
|
||||||
),
|
),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
);
|
);
|
||||||
@ -157,6 +170,7 @@ void main() {
|
|||||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12
|
// 0 1 2 3 4 5 6 7 8 9 10 11 12
|
||||||
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
|
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
|
||||||
),
|
),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
layout(paragraph, constraints: const BoxConstraints(maxWidth: 100.0));
|
layout(paragraph, constraints: const BoxConstraints(maxWidth: 100.0));
|
||||||
void layoutAt(int maxLines) {
|
void layoutAt(int maxLines) {
|
||||||
@ -183,6 +197,7 @@ void main() {
|
|||||||
text: 'Hello',
|
text: 'Hello',
|
||||||
style: const TextStyle(color: const Color(0xFF000000)),
|
style: const TextStyle(color: const Color(0xFF000000)),
|
||||||
),
|
),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
layout(paragraph, constraints: const BoxConstraints(maxWidth: 100.0), phase: EnginePhase.paint);
|
layout(paragraph, constraints: const BoxConstraints(maxWidth: 100.0), phase: EnginePhase.paint);
|
||||||
expect(paragraph.debugNeedsLayout, isFalse);
|
expect(paragraph.debugNeedsLayout, isFalse);
|
||||||
@ -210,15 +225,21 @@ void main() {
|
|||||||
test('toStringDeep', () {
|
test('toStringDeep', () {
|
||||||
final RenderParagraph paragraph = new RenderParagraph(
|
final RenderParagraph paragraph = new RenderParagraph(
|
||||||
const TextSpan(text: _kText),
|
const TextSpan(text: _kText),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
expect(paragraph, hasAGoodToStringDeep);
|
expect(paragraph, hasAGoodToStringDeep);
|
||||||
expect(
|
expect(
|
||||||
paragraph.toStringDeep(),
|
paragraph.toStringDeep(),
|
||||||
equalsIgnoringHashCodes(
|
equalsIgnoringHashCodes(
|
||||||
'RenderParagraph#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
'RenderParagraph#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
||||||
' │ parentData: null\n'
|
' │ parentData: MISSING\n'
|
||||||
' │ constraints: null\n'
|
' │ constraints: MISSING\n'
|
||||||
' │ size: MISSING\n'
|
' │ size: MISSING\n'
|
||||||
|
' │ textAlign: start\n'
|
||||||
|
' │ textDirection: ltr\n'
|
||||||
|
' │ softWrap: wrapping at box width\n'
|
||||||
|
' │ overflow: clip\n'
|
||||||
|
' │ maxLines: unlimited\n'
|
||||||
' ╘═╦══ text ═══\n'
|
' ╘═╦══ text ═══\n'
|
||||||
' ║ TextSpan:\n'
|
' ║ TextSpan:\n'
|
||||||
' ║ "I polished up that handle so carefullee\n'
|
' ║ "I polished up that handle so carefullee\n'
|
||||||
|
@ -16,7 +16,12 @@ int countSemanticsChildren(RenderObject object) {
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('RenderOpacity and children and semantics', () {
|
test('RenderOpacity and children and semantics', () {
|
||||||
final RenderOpacity box = new RenderOpacity(child: new RenderParagraph(const TextSpan()));
|
final RenderOpacity box = new RenderOpacity(
|
||||||
|
child: new RenderParagraph(
|
||||||
|
const TextSpan(),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
),
|
||||||
|
);
|
||||||
expect(countSemanticsChildren(box), 1);
|
expect(countSemanticsChildren(box), 1);
|
||||||
box.opacity = 0.5;
|
box.opacity = 0.5;
|
||||||
expect(countSemanticsChildren(box), 1);
|
expect(countSemanticsChildren(box), 1);
|
||||||
|
@ -19,8 +19,8 @@ void main() {
|
|||||||
root.toStringDeep(),
|
root.toStringDeep(),
|
||||||
equalsIgnoringHashCodes(
|
equalsIgnoringHashCodes(
|
||||||
'RenderViewport#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
'RenderViewport#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
||||||
' parentData: null\n'
|
' parentData: MISSING\n'
|
||||||
' constraints: null\n'
|
' constraints: MISSING\n'
|
||||||
' size: MISSING\n'
|
' size: MISSING\n'
|
||||||
' axisDirection: down\n'
|
' axisDirection: down\n'
|
||||||
' crossAxisDirection: right\n'
|
' crossAxisDirection: right\n'
|
||||||
|
@ -13,8 +13,8 @@ void main() {
|
|||||||
renderWrap.toStringDeep(),
|
renderWrap.toStringDeep(),
|
||||||
equalsIgnoringHashCodes(
|
equalsIgnoringHashCodes(
|
||||||
'RenderWrap#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
'RenderWrap#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
|
||||||
' parentData: null\n'
|
' parentData: MISSING\n'
|
||||||
' constraints: null\n'
|
' constraints: MISSING\n'
|
||||||
' size: MISSING\n'
|
' size: MISSING\n'
|
||||||
' direction: horizontal\n'
|
' direction: horizontal\n'
|
||||||
' alignment: start\n'
|
' alignment: start\n'
|
||||||
|
@ -230,7 +230,7 @@ void main() {
|
|||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
width: 100.0,
|
width: 100.0,
|
||||||
height: 100.0,
|
height: 100.0,
|
||||||
child: const Text('X')
|
child: const Text('X', textDirection: TextDirection.ltr)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -250,7 +250,7 @@ void main() {
|
|||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
width: 200.0,
|
width: 200.0,
|
||||||
height: 200.0,
|
height: 200.0,
|
||||||
child: const Text('X')
|
child: const Text('X', textDirection: TextDirection.ltr)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -274,7 +274,7 @@ void main() {
|
|||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
width: 200.0,
|
width: 200.0,
|
||||||
height: 100.0,
|
height: 100.0,
|
||||||
child: const Text('X')
|
child: const Text('X', textDirection: TextDirection.ltr)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -302,16 +302,16 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('AnimatedCrossFade.layoutBuilder', (WidgetTester tester) async {
|
testWidgets('AnimatedCrossFade.layoutBuilder', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const AnimatedCrossFade(
|
await tester.pumpWidget(const AnimatedCrossFade(
|
||||||
firstChild: const Text('AAA'),
|
firstChild: const Text('AAA', textDirection: TextDirection.ltr),
|
||||||
secondChild: const Text('BBB'),
|
secondChild: const Text('BBB', textDirection: TextDirection.ltr),
|
||||||
crossFadeState: CrossFadeState.showFirst,
|
crossFadeState: CrossFadeState.showFirst,
|
||||||
duration: const Duration(milliseconds: 50),
|
duration: const Duration(milliseconds: 50),
|
||||||
));
|
));
|
||||||
expect(find.text('AAA'), findsOneWidget);
|
expect(find.text('AAA'), findsOneWidget);
|
||||||
expect(find.text('BBB'), findsOneWidget);
|
expect(find.text('BBB'), findsOneWidget);
|
||||||
await tester.pumpWidget(new AnimatedCrossFade(
|
await tester.pumpWidget(new AnimatedCrossFade(
|
||||||
firstChild: const Text('AAA'),
|
firstChild: const Text('AAA', textDirection: TextDirection.ltr),
|
||||||
secondChild: const Text('BBB'),
|
secondChild: const Text('BBB', textDirection: TextDirection.ltr),
|
||||||
crossFadeState: CrossFadeState.showFirst,
|
crossFadeState: CrossFadeState.showFirst,
|
||||||
duration: const Duration(milliseconds: 50),
|
duration: const Duration(milliseconds: 50),
|
||||||
layoutBuilder: (Widget a, Key aKey, Widget b, Key bKey) => a,
|
layoutBuilder: (Widget a, Key aKey, Widget b, Key bKey) => a,
|
||||||
@ -319,8 +319,8 @@ void main() {
|
|||||||
expect(find.text('AAA'), findsOneWidget);
|
expect(find.text('AAA'), findsOneWidget);
|
||||||
expect(find.text('BBB'), findsNothing);
|
expect(find.text('BBB'), findsNothing);
|
||||||
await tester.pumpWidget(new AnimatedCrossFade(
|
await tester.pumpWidget(new AnimatedCrossFade(
|
||||||
firstChild: const Text('AAA'),
|
firstChild: const Text('AAA', textDirection: TextDirection.ltr),
|
||||||
secondChild: const Text('BBB'),
|
secondChild: const Text('BBB', textDirection: TextDirection.ltr),
|
||||||
crossFadeState: CrossFadeState.showSecond,
|
crossFadeState: CrossFadeState.showSecond,
|
||||||
duration: const Duration(milliseconds: 50),
|
duration: const Duration(milliseconds: 50),
|
||||||
layoutBuilder: (Widget a, Key aKey, Widget b, Key bKey) => a,
|
layoutBuilder: (Widget a, Key aKey, Widget b, Key bKey) => a,
|
||||||
|
@ -121,7 +121,7 @@ void main() {
|
|||||||
child: new SizedBox(
|
child: new SizedBox(
|
||||||
height: 100.0,
|
height: 100.0,
|
||||||
child: new Center(
|
child: new Center(
|
||||||
child: new Text('item $item'),
|
child: new Text('item $item', textDirection: TextDirection.ltr),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart';
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
Widget snapshotText(BuildContext context, AsyncSnapshot<String> snapshot) {
|
Widget snapshotText(BuildContext context, AsyncSnapshot<String> snapshot) {
|
||||||
return new Text(snapshot.toString());
|
return new Text(snapshot.toString(), textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
group('AsyncSnapshot', () {
|
group('AsyncSnapshot', () {
|
||||||
test('requiring data succeeds if data is present', () {
|
test('requiring data succeeds if data is present', () {
|
||||||
@ -280,5 +280,5 @@ class StringCollector extends StreamBuilderBase<String, List<String>> {
|
|||||||
List<String> afterDisconnected(List<String> current) => current..add('disc');
|
List<String> afterDisconnected(List<String> current) => current..add('disc');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, List<String> currentSummary) => new Text(currentSummary.join(', '));
|
Widget build(BuildContext context, List<String> currentSummary) => new Text(currentSummary.join(', '), textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@ class TestCanvas implements Canvas {
|
|||||||
void main() {
|
void main() {
|
||||||
test('A Banner with a location of topLeft paints in the top left', () {
|
test('A Banner with a location of topLeft paints in the top left', () {
|
||||||
final BannerPainter bannerPainter = new BannerPainter(
|
final BannerPainter bannerPainter = new BannerPainter(
|
||||||
message:"foo",
|
message: 'foo',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
location: BannerLocation.topLeft
|
location: BannerLocation.topLeft
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -45,7 +46,8 @@ void main() {
|
|||||||
|
|
||||||
test('A Banner with a location of topRight paints in the top right', () {
|
test('A Banner with a location of topRight paints in the top right', () {
|
||||||
final BannerPainter bannerPainter = new BannerPainter(
|
final BannerPainter bannerPainter = new BannerPainter(
|
||||||
message:"foo",
|
message: 'foo',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
location: BannerLocation.topRight
|
location: BannerLocation.topRight
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -71,7 +73,8 @@ void main() {
|
|||||||
|
|
||||||
test('A Banner with a location of bottomLeft paints in the bottom left', () {
|
test('A Banner with a location of bottomLeft paints in the bottom left', () {
|
||||||
final BannerPainter bannerPainter = new BannerPainter(
|
final BannerPainter bannerPainter = new BannerPainter(
|
||||||
message:"foo",
|
message: 'foo',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
location: BannerLocation.bottomLeft
|
location: BannerLocation.bottomLeft
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -97,7 +100,8 @@ void main() {
|
|||||||
|
|
||||||
test('A Banner with a location of bottomRight paints in the bottom right', () {
|
test('A Banner with a location of bottomRight paints in the bottom right', () {
|
||||||
final BannerPainter bannerPainter = new BannerPainter(
|
final BannerPainter bannerPainter = new BannerPainter(
|
||||||
message:"foo",
|
message: 'foo',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
location: BannerLocation.bottomRight
|
location: BannerLocation.bottomRight
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ void main() {
|
|||||||
fontFamily: 'Ahem',
|
fontFamily: 'Ahem',
|
||||||
fontSize: 100.0,
|
fontSize: 100.0,
|
||||||
),
|
),
|
||||||
child: const Text('X'),
|
child: const Text('X', textDirection: TextDirection.ltr),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -32,7 +32,7 @@ void main() {
|
|||||||
fontFamily: 'Ahem',
|
fontFamily: 'Ahem',
|
||||||
fontSize: 100.0,
|
fontSize: 100.0,
|
||||||
),
|
),
|
||||||
child: const Text('X'),
|
child: const Text('X', textDirection: TextDirection.ltr),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -21,9 +21,9 @@ Widget buildFrame(ScrollPhysics physics) {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const SizedBox(height: 100.0, child: const Text('top')),
|
const SizedBox(height: 100.0, child: const Text('top', textDirection: TextDirection.ltr)),
|
||||||
new Expanded(child: new Container()),
|
new Expanded(child: new Container()),
|
||||||
const SizedBox(height: 100.0, child: const Text('bottom')),
|
const SizedBox(height: 100.0, child: const Text('bottom', textDirection: TextDirection.ltr)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart';
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('DefaultTextStyle changes propagate to Text', (WidgetTester tester) async {
|
testWidgets('DefaultTextStyle changes propagate to Text', (WidgetTester tester) async {
|
||||||
const Text textWidget = const Text('Hello');
|
const Text textWidget = const Text('Hello', textDirection: TextDirection.ltr);
|
||||||
const TextStyle s1 = const TextStyle(
|
const TextStyle s1 = const TextStyle(
|
||||||
fontSize: 10.0,
|
fontSize: 10.0,
|
||||||
fontWeight: FontWeight.w800,
|
fontWeight: FontWeight.w800,
|
||||||
@ -16,7 +16,7 @@ void main() {
|
|||||||
|
|
||||||
await tester.pumpWidget(const DefaultTextStyle(
|
await tester.pumpWidget(const DefaultTextStyle(
|
||||||
style: s1,
|
style: s1,
|
||||||
child: textWidget
|
child: textWidget,
|
||||||
));
|
));
|
||||||
|
|
||||||
RichText text = tester.firstWidget(find.byType(RichText));
|
RichText text = tester.firstWidget(find.byType(RichText));
|
||||||
|
@ -14,7 +14,9 @@ List<int> dismissedItems = <int>[];
|
|||||||
Widget background;
|
Widget background;
|
||||||
|
|
||||||
Widget buildTest({ double startToEndThreshold }) {
|
Widget buildTest({ double startToEndThreshold }) {
|
||||||
return new StatefulBuilder(
|
return new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new StatefulBuilder(
|
||||||
builder: (BuildContext context, StateSetter setState) {
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
Widget buildDismissibleItem(int item) {
|
Widget buildDismissibleItem(int item) {
|
||||||
return new Dismissible(
|
return new Dismissible(
|
||||||
@ -56,6 +58,7 @@ Widget buildTest({ double startToEndThreshold }) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +290,10 @@ void main() {
|
|||||||
// actually remove the dismissed widget, which is a violation of the
|
// actually remove the dismissed widget, which is a violation of the
|
||||||
// Dismissible contract. This is not an example of good practice.
|
// Dismissible contract. This is not an example of good practice.
|
||||||
testWidgets('dismissing bottom then top (smoketest)', (WidgetTester tester) async {
|
testWidgets('dismissing bottom then top (smoketest)', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(new Center(
|
await tester.pumpWidget(
|
||||||
|
new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Center(
|
||||||
child: new Container(
|
child: new Container(
|
||||||
width: 100.0,
|
width: 100.0,
|
||||||
height: 1000.0,
|
height: 1000.0,
|
||||||
@ -298,7 +304,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
));
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
expect(find.text('1'), findsOneWidget);
|
expect(find.text('1'), findsOneWidget);
|
||||||
expect(find.text('2'), findsOneWidget);
|
expect(find.text('2'), findsOneWidget);
|
||||||
await dismissElement(tester, find.text('2'), gestureDirection: DismissDirection.startToEnd);
|
await dismissElement(tester, find.text('2'), gestureDirection: DismissDirection.startToEnd);
|
||||||
|
@ -24,7 +24,7 @@ class TestWidgetState extends State<TestWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => const Text('test');
|
Widget build(BuildContext context) => const Text('test', textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
@ -28,7 +28,7 @@ void main() {
|
|||||||
width: 100.0,
|
width: 100.0,
|
||||||
height: 100.0,
|
height: 100.0,
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: const Text('X'),
|
child: const Text('X', textDirection: TextDirection.ltr),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -63,8 +63,8 @@ void main() {
|
|||||||
new Row(
|
new Row(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Expanded(flex: null, child: const Text('one')),
|
const Expanded(flex: null, child: const Text('one', textDirection: TextDirection.ltr)),
|
||||||
const Flexible(flex: null, child: const Text('two')),
|
const Flexible(flex: null, child: const Text('two', textDirection: TextDirection.ltr)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -44,7 +44,7 @@ void main() {
|
|||||||
width: 100.0,
|
width: 100.0,
|
||||||
height: 100.0,
|
height: 100.0,
|
||||||
color: const Color(0xFF0000FF),
|
color: const Color(0xFF0000FF),
|
||||||
child: new Text('$i')
|
child: new Text('$i', textDirection: TextDirection.ltr)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ class TestFocusableState extends State<TestFocusable> {
|
|||||||
child: new AnimatedBuilder(
|
child: new AnimatedBuilder(
|
||||||
animation: focusNode,
|
animation: focusNode,
|
||||||
builder: (BuildContext context, Widget child) {
|
builder: (BuildContext context, Widget child) {
|
||||||
return new Text(focusNode.hasFocus ? widget.yes : widget.no);
|
return new Text(focusNode.hasFocus ? widget.yes : widget.no, textDirection: TextDirection.ltr);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -11,7 +11,9 @@ void main() {
|
|||||||
String fieldValue;
|
String fieldValue;
|
||||||
|
|
||||||
Widget builder() {
|
Widget builder() {
|
||||||
return new Center(
|
return new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Center(
|
||||||
child: new Material(
|
child: new Material(
|
||||||
child: new Form(
|
child: new Form(
|
||||||
key: formKey,
|
key: formKey,
|
||||||
@ -20,6 +22,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +45,9 @@ void main() {
|
|||||||
String fieldValue;
|
String fieldValue;
|
||||||
|
|
||||||
Widget builder() {
|
Widget builder() {
|
||||||
return new Center(
|
return new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Center(
|
||||||
child: new Material(
|
child: new Material(
|
||||||
child: new Form(
|
child: new Form(
|
||||||
child: new TextField(
|
child: new TextField(
|
||||||
@ -50,6 +55,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +78,9 @@ void main() {
|
|||||||
String errorText(String value) => value + '/error';
|
String errorText(String value) => value + '/error';
|
||||||
|
|
||||||
Widget builder(bool autovalidate) {
|
Widget builder(bool autovalidate) {
|
||||||
return new Center(
|
return new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Center(
|
||||||
child: new Material(
|
child: new Material(
|
||||||
child: new Form(
|
child: new Form(
|
||||||
key: formKey,
|
key: formKey,
|
||||||
@ -82,6 +90,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +171,9 @@ void main() {
|
|||||||
final GlobalKey<FormFieldState<String>> inputKey = new GlobalKey<FormFieldState<String>>();
|
final GlobalKey<FormFieldState<String>> inputKey = new GlobalKey<FormFieldState<String>>();
|
||||||
|
|
||||||
Widget builder() {
|
Widget builder() {
|
||||||
return new Center(
|
return new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Center(
|
||||||
child: new Material(
|
child: new Material(
|
||||||
child: new Form(
|
child: new Form(
|
||||||
child: new TextFormField(
|
child: new TextFormField(
|
||||||
@ -171,6 +182,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +210,9 @@ void main() {
|
|||||||
String fieldValue;
|
String fieldValue;
|
||||||
|
|
||||||
Widget builder(bool remove) {
|
Widget builder(bool remove) {
|
||||||
return new Center(
|
return new Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: new Center(
|
||||||
child: new Material(
|
child: new Material(
|
||||||
child: new Form(
|
child: new Form(
|
||||||
key: formKey,
|
key: formKey,
|
||||||
@ -209,6 +223,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class StatefulLeafState extends State<StatefulLeaf> {
|
|||||||
void markNeedsBuild() { setState(() { }); }
|
void markNeedsBuild() { setState(() { }); }
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => const Text('leaf');
|
Widget build(BuildContext context) => const Text('leaf', textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
|
|
||||||
class KeyedWrapper extends StatelessWidget {
|
class KeyedWrapper extends StatelessWidget {
|
||||||
|
@ -8,7 +8,7 @@ import 'package:flutter/widgets.dart';
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('toString control test', (WidgetTester tester) async {
|
testWidgets('toString control test', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const Center(child: const Text('Hello')));
|
await tester.pumpWidget(const Center(child: const Text('Hello', textDirection: TextDirection.ltr)));
|
||||||
final HitTestResult result = tester.hitTestOnBinding(Offset.zero);
|
final HitTestResult result = tester.hitTestOnBinding(Offset.zero);
|
||||||
expect(result, hasOneLineDescription);
|
expect(result, hasOneLineDescription);
|
||||||
expect(result.path.first, hasOneLineDescription);
|
expect(result.path.first, hasOneLineDescription);
|
||||||
|
@ -26,6 +26,7 @@ void main() {
|
|||||||
new Center(
|
new Center(
|
||||||
child: new RichText(
|
child: new RichText(
|
||||||
key: textKey,
|
key: textKey,
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
text: new TextSpan(
|
text: new TextSpan(
|
||||||
children: <TextSpan>[
|
children: <TextSpan>[
|
||||||
new TextSpan(
|
new TextSpan(
|
||||||
|
@ -8,13 +8,16 @@ import 'package:flutter/widgets.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
testWidgets('Can set opacity for an Icon', (WidgetTester tester) async {
|
testWidgets('Can set opacity for an Icon', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const IconTheme(
|
const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: const IconTheme(
|
||||||
data: const IconThemeData(
|
data: const IconThemeData(
|
||||||
color: const Color(0xFF666666),
|
color: const Color(0xFF666666),
|
||||||
opacity: 0.5
|
opacity: 0.5
|
||||||
),
|
),
|
||||||
child: const Icon(const IconData(0xd0a0, fontFamily: 'Arial'))
|
child: const Icon(const IconData(0xd0a0, fontFamily: 'Arial'))
|
||||||
)
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
final RichText text = tester.widget(find.byType(RichText));
|
final RichText text = tester.widget(find.byType(RichText));
|
||||||
expect(text.text.style.color, const Color(0xFF666666).withOpacity(0.5));
|
expect(text.text.style.color, const Color(0xFF666666).withOpacity(0.5));
|
||||||
@ -22,9 +25,12 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Icon sizing - no theme, default size', (WidgetTester tester) async {
|
testWidgets('Icon sizing - no theme, default size', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const Center(
|
const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: const Center(
|
||||||
child: const Icon(null),
|
child: const Icon(null),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
|
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
|
||||||
@ -33,12 +39,15 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Icon sizing - no theme, explicit size', (WidgetTester tester) async {
|
testWidgets('Icon sizing - no theme, explicit size', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const Center(
|
const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: const Center(
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
null,
|
null,
|
||||||
size: 96.0,
|
size: 96.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
|
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
|
||||||
@ -47,12 +56,15 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Icon sizing - sized theme', (WidgetTester tester) async {
|
testWidgets('Icon sizing - sized theme', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const Center(
|
const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: const Center(
|
||||||
child: const IconTheme(
|
child: const IconTheme(
|
||||||
data: const IconThemeData(size: 36.0),
|
data: const IconThemeData(size: 36.0),
|
||||||
child: const Icon(null),
|
child: const Icon(null),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
|
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
|
||||||
@ -61,7 +73,9 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Icon sizing - sized theme, explicit size', (WidgetTester tester) async {
|
testWidgets('Icon sizing - sized theme, explicit size', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const Center(
|
const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: const Center(
|
||||||
child: const IconTheme(
|
child: const IconTheme(
|
||||||
data: const IconThemeData(size: 36.0),
|
data: const IconThemeData(size: 36.0),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
@ -69,6 +83,7 @@ void main() {
|
|||||||
size: 48.0,
|
size: 48.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -78,12 +93,15 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Icon sizing - sizeless theme, default size', (WidgetTester tester) async {
|
testWidgets('Icon sizing - sizeless theme, default size', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const Center(
|
const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: const Center(
|
||||||
child: const IconTheme(
|
child: const IconTheme(
|
||||||
data: const IconThemeData(),
|
data: const IconThemeData(),
|
||||||
child: const Icon(null),
|
child: const Icon(null),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
|
final RenderBox renderObject = tester.renderObject(find.byType(Icon));
|
||||||
@ -93,9 +111,12 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('Icon with custom font', (WidgetTester tester) async {
|
testWidgets('Icon with custom font', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const Center(
|
const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: const Center(
|
||||||
child: const Icon(const IconData(0x41, fontFamily: 'Roboto')),
|
child: const Icon(const IconData(0x41, fontFamily: 'Roboto')),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final RichText richText = tester.firstWidget(find.byType(RichText));
|
final RichText richText = tester.firstWidget(find.byType(RichText));
|
||||||
|
@ -92,7 +92,7 @@ class TriggerableState extends State<TriggerableWidget> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
widget.counter.count++;
|
widget.counter.count++;
|
||||||
return new Text("Bang $_count!");
|
return new Text("Bang $_count!", textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ void main() {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
||||||
log.add('a: ${v.value}');
|
log.add('a: ${v.value}');
|
||||||
return const Text('');
|
return const Text('', textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -146,7 +146,7 @@ void main() {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
||||||
log.add('b: ${v.value}');
|
log.add('b: ${v.value}');
|
||||||
return const Text('');
|
return const Text('', textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -204,7 +204,7 @@ void main() {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
||||||
log.add('a: ${v.value}');
|
log.add('a: ${v.value}');
|
||||||
return const Text('');
|
return const Text('', textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -222,7 +222,7 @@ void main() {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
||||||
log.add('b: ${v.value}');
|
log.add('b: ${v.value}');
|
||||||
return const Text('');
|
return const Text('', textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -265,7 +265,7 @@ void main() {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
||||||
log.add(v.value);
|
log.add(v.value);
|
||||||
return const Text('');
|
return const Text('', textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -336,7 +336,7 @@ void main() {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
|
||||||
log.add(v.value);
|
log.add(v.value);
|
||||||
return const Text('');
|
return const Text('', textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ void main() {
|
|||||||
key: new ValueKey<int>(index),
|
key: new ValueKey<int>(index),
|
||||||
width: 500.0, // this should be ignored
|
width: 500.0, // this should be ignored
|
||||||
height: 400.0, // should be overridden by itemExtent
|
height: 400.0, // should be overridden by itemExtent
|
||||||
child: new Text('$index')
|
child: new Text('$index', textDirection: TextDirection.ltr)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ void main() {
|
|||||||
itemExtent: 200.0,
|
itemExtent: 200.0,
|
||||||
itemBuilder: itemBuilder,
|
itemBuilder: itemBuilder,
|
||||||
),
|
),
|
||||||
right: const Text('Not Today')
|
right: const Text('Not Today'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -190,7 +190,7 @@ void main() {
|
|||||||
|
|
||||||
final IndexedWidgetBuilder itemBuilder = (BuildContext context, int index) {
|
final IndexedWidgetBuilder itemBuilder = (BuildContext context, int index) {
|
||||||
callbackTracker.add(index);
|
callbackTracker.add(index);
|
||||||
return new Text('$index', key: new ValueKey<int>(index));
|
return new Text('$index', key: new ValueKey<int>(index), textDirection: TextDirection.ltr);
|
||||||
};
|
};
|
||||||
|
|
||||||
final Widget testWidget = new Directionality(
|
final Widget testWidget = new Directionality(
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
const List<int> items = const <int>[0, 1, 2, 3, 4, 5];
|
const List<int> items = const <int>[0, 1, 2, 3, 4, 5];
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user