mirror of
https://github.com/flutter/flutter
synced 2024-09-13 21:32:11 +00:00
Minor fix compendium (#107874)
This commit is contained in:
parent
da88c953c7
commit
9b2668a451
|
@ -4,4 +4,4 @@ The [snippets] tool uses the files in the `skeletons` directory to inject code
|
|||
blocks generated from `{@tool dartpad}`, `{@tool sample}`, and `{@tool snippet}`
|
||||
sections found in doc comments into the API docs.
|
||||
|
||||
[snippet]: https://github.com/flutter/assets-for-api-docs/tree/master/packages/snippets
|
||||
[snippets]: https://github.com/flutter/assets-for-api-docs/tree/master/packages/snippets
|
||||
|
|
|
@ -30,6 +30,10 @@ that on an Android device like so:
|
|||
|
||||
## Naming
|
||||
|
||||
> `lib/library/file/class_name.n.dart`
|
||||
>
|
||||
> `lib/library/file/class_name.member_name.n.dart`
|
||||
|
||||
The naming scheme for the files is similar to the hierarchy under
|
||||
[packages/flutter/lib/src](../../packages/flutter/lib/src), except that the
|
||||
files are represented as directories (without the `.dart` suffix), and each
|
||||
|
|
26
examples/api/lib/widgets/app/widgets_app.widgets_app.0.dart
Normal file
26
examples/api/lib/widgets/app/widgets_app.widgets_app.0.dart
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flutter code sample for WidgetsApp
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void main() => runApp(const MyApp());
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WidgetsApp(
|
||||
title: 'Example',
|
||||
color: const Color(0xFF000000),
|
||||
home: const Center(child: Text('Hello World')),
|
||||
pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) => PageRouteBuilder<T>(
|
||||
settings: settings,
|
||||
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => builder(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2014 The Flutter 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_api_samples/widgets/app/widgets_app.widgets_app.0.dart' as example;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('WidgetsApp test', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.MyApp(),
|
||||
);
|
||||
|
||||
expect(find.text('Hello World'), findsOneWidget);
|
||||
});
|
||||
}
|
|
@ -19,6 +19,10 @@ import 'localizations.dart';
|
|||
/// assert(debugCheckHasCupertinoLocalizations(context));
|
||||
/// ```
|
||||
///
|
||||
/// Always place this before any early returns, so that the invariant is checked
|
||||
/// in all cases. This prevents bugs from hiding until a particular codepath is
|
||||
/// hit.
|
||||
///
|
||||
/// Does nothing if asserts are disabled. Always returns true.
|
||||
bool debugCheckHasCupertinoLocalizations(BuildContext context) {
|
||||
assert(() {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
import 'package:flutter/foundation.dart' show ValueListenable, clampDouble;
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'button.dart';
|
||||
|
@ -32,8 +31,11 @@ const CupertinoDynamicColor _kToolbarBackgroundColor = CupertinoDynamicColor.wit
|
|||
darkColor: Color(0xff302928),
|
||||
);
|
||||
|
||||
|
||||
class _CupertinoDesktopTextSelectionControls extends TextSelectionControls {
|
||||
/// Desktop Cupertino styled text selection controls.
|
||||
///
|
||||
/// The [cupertinoDesktopTextSelectionControls] global variable has a
|
||||
/// suitable instance of this class.
|
||||
class CupertinoDesktopTextSelectionControls extends TextSelectionControls {
|
||||
/// Desktop has no text selection handles.
|
||||
@override
|
||||
Size getHandleSize(double textLineHeight) {
|
||||
|
@ -87,7 +89,7 @@ class _CupertinoDesktopTextSelectionControls extends TextSelectionControls {
|
|||
|
||||
/// Text selection controls that follows Mac design conventions.
|
||||
final TextSelectionControls cupertinoDesktopTextSelectionControls =
|
||||
_CupertinoDesktopTextSelectionControls();
|
||||
CupertinoDesktopTextSelectionControls();
|
||||
|
||||
// Generates the child that's passed into CupertinoDesktopTextSelectionToolbar.
|
||||
class _CupertinoDesktopTextSelectionControlsToolbar extends StatefulWidget {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/foundation.dart' show ValueListenable, clampDouble;
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'localizations.dart';
|
||||
|
@ -192,6 +191,9 @@ class _TextSelectionHandlePainter extends CustomPainter {
|
|||
}
|
||||
|
||||
/// iOS Cupertino styled text selection controls.
|
||||
///
|
||||
/// The [cupertinoTextSelectionControls] global variable has a
|
||||
/// suitable instance of this class.
|
||||
class CupertinoTextSelectionControls extends TextSelectionControls {
|
||||
/// Returns the size of the Cupertino handle.
|
||||
@override
|
||||
|
|
|
@ -23,6 +23,10 @@ import 'scaffold.dart' show Scaffold, ScaffoldMessenger;
|
|||
/// assert(debugCheckHasMaterial(context));
|
||||
/// ```
|
||||
///
|
||||
/// Always place this before any early returns, so that the invariant is checked
|
||||
/// in all cases. This prevents bugs from hiding until a particular codepath is
|
||||
/// hit.
|
||||
///
|
||||
/// This method can be expensive (it walks the element tree).
|
||||
///
|
||||
/// Does nothing if asserts are disabled. Always returns true.
|
||||
|
@ -67,6 +71,10 @@ bool debugCheckHasMaterial(BuildContext context) {
|
|||
/// assert(debugCheckHasMaterialLocalizations(context));
|
||||
/// ```
|
||||
///
|
||||
/// Always place this before any early returns, so that the invariant is checked
|
||||
/// in all cases. This prevents bugs from hiding until a particular codepath is
|
||||
/// hit.
|
||||
///
|
||||
/// This function has the side-effect of establishing an inheritance
|
||||
/// relationship with the nearest [Localizations] widget (see
|
||||
/// [BuildContext.dependOnInheritedWidgetOfExactType]). This is ok if the caller
|
||||
|
@ -112,6 +120,10 @@ bool debugCheckHasMaterialLocalizations(BuildContext context) {
|
|||
/// assert(debugCheckHasScaffold(context));
|
||||
/// ```
|
||||
///
|
||||
/// Always place this before any early returns, so that the invariant is checked
|
||||
/// in all cases. This prevents bugs from hiding until a particular codepath is
|
||||
/// hit.
|
||||
///
|
||||
/// This method can be expensive (it walks the element tree).
|
||||
///
|
||||
/// Does nothing if asserts are disabled. Always returns true.
|
||||
|
@ -145,6 +157,10 @@ bool debugCheckHasScaffold(BuildContext context) {
|
|||
/// assert(debugCheckHasScaffoldMessenger(context));
|
||||
/// ```
|
||||
///
|
||||
/// Always place this before any early returns, so that the invariant is checked
|
||||
/// in all cases. This prevents bugs from hiding until a particular codepath is
|
||||
/// hit.
|
||||
///
|
||||
/// This method can be expensive (it walks the element tree).
|
||||
///
|
||||
/// Does nothing if asserts are disabled. Always returns true.
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart' show ValueListenable, clampDouble;
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
|
@ -19,7 +18,11 @@ import 'theme.dart';
|
|||
const double _kToolbarScreenPadding = 8.0;
|
||||
const double _kToolbarWidth = 222.0;
|
||||
|
||||
class _DesktopTextSelectionControls extends TextSelectionControls {
|
||||
/// Desktop Material styled text selection controls.
|
||||
///
|
||||
/// The [desktopTextSelectionControls] global variable has a
|
||||
/// suitable instance of this class.
|
||||
class DesktopTextSelectionControls extends TextSelectionControls {
|
||||
/// Desktop has no text selection handles.
|
||||
@override
|
||||
Size getHandleSize(double textLineHeight) {
|
||||
|
@ -83,7 +86,7 @@ class _DesktopTextSelectionControls extends TextSelectionControls {
|
|||
|
||||
/// Text selection controls that loosely follows Material design conventions.
|
||||
final TextSelectionControls desktopTextSelectionControls =
|
||||
_DesktopTextSelectionControls();
|
||||
DesktopTextSelectionControls();
|
||||
|
||||
// Generates the child that's passed into DesktopTextSelectionToolbar.
|
||||
class _DesktopTextSelectionControlsToolbar extends StatefulWidget {
|
||||
|
@ -145,12 +148,14 @@ class _DesktopTextSelectionControlsToolbarState extends State<_DesktopTextSelect
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
assert(debugCheckHasMediaQuery(context));
|
||||
|
||||
// Don't render the menu until the state of the clipboard is known.
|
||||
if (widget.handlePaste != null && widget.clipboardStatus?.value == ClipboardStatus.unknown) {
|
||||
return const SizedBox(width: 0.0, height: 0.0);
|
||||
}
|
||||
|
||||
assert(debugCheckHasMediaQuery(context));
|
||||
final MediaQueryData mediaQuery = MediaQuery.of(context);
|
||||
|
||||
final Offset midpointAnchor = Offset(
|
||||
|
@ -161,7 +166,6 @@ class _DesktopTextSelectionControlsToolbarState extends State<_DesktopTextSelect
|
|||
widget.selectionMidpoint.dy - widget.globalEditableRegion.top,
|
||||
);
|
||||
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
|
||||
final List<Widget> items = <Widget>[];
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import 'debug.dart';
|
||||
import 'desktop_text_selection.dart';
|
||||
import 'magnifier.dart';
|
||||
import 'text_selection.dart';
|
||||
|
@ -19,6 +20,10 @@ import 'theme.dart';
|
|||
/// a specific screen, consider wrapping the body of the [Route] with a
|
||||
/// [SelectionArea].
|
||||
///
|
||||
/// The [SelectionArea] widget must have a [Localizations] ancestor that
|
||||
/// contains a [MaterialLocalizations] delegate; using the [MaterialApp] widget
|
||||
/// ensures that such an ancestor is present.
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This example shows how to make a screen selectable.
|
||||
///
|
||||
|
@ -85,6 +90,7 @@ class _SelectionAreaState extends State<SelectionArea> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
TextSelectionControls? controls = widget.selectionControls;
|
||||
switch (Theme.of(context).platform) {
|
||||
case TargetPlatform.android:
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'debug.dart';
|
||||
|
@ -22,6 +21,9 @@ const double _kToolbarContentDistanceBelow = _kHandleSize - 2.0;
|
|||
const double _kToolbarContentDistance = 8.0;
|
||||
|
||||
/// Android Material styled text selection controls.
|
||||
///
|
||||
/// The [materialTextSelectionControls] global variable has a
|
||||
/// suitable instance of this class.
|
||||
class MaterialTextSelectionControls extends TextSelectionControls {
|
||||
/// Returns the size of the Material handle.
|
||||
@override
|
||||
|
|
|
@ -420,6 +420,8 @@ class RenderParagraph extends RenderBox
|
|||
}
|
||||
|
||||
/// The color to use when painting the selection.
|
||||
///
|
||||
/// Ignored if the text is not selectable (e.g. if [registrar] is null).
|
||||
Color? get selectionColor => _selectionColor;
|
||||
Color? _selectionColor;
|
||||
set selectionColor(Color? value) {
|
||||
|
|
|
@ -296,15 +296,21 @@ class WidgetsApp extends StatefulWidget {
|
|||
/// and [builder] are null, or if they fail to create a requested route,
|
||||
/// [onGenerateRoute] will be invoked. If that fails, [onUnknownRoute] will be invoked.
|
||||
///
|
||||
/// The [pageRouteBuilder] will create a [PageRoute] that wraps newly built routes.
|
||||
/// The [pageRouteBuilder] is called to create a [PageRoute] that wraps newly built routes.
|
||||
/// If the [builder] is non-null and the [onGenerateRoute] argument is null, then the
|
||||
/// [builder] will not be provided only with the context and the child widget, whereas
|
||||
/// the [pageRouteBuilder] will be provided with [RouteSettings]. If [onGenerateRoute]
|
||||
/// is not provided, [navigatorKey], [onUnknownRoute], [navigatorObservers], and
|
||||
/// [initialRoute] must have their default values, as they will have no effect.
|
||||
/// [builder] will be provided only with the context and the child widget, whereas
|
||||
/// the [pageRouteBuilder] will be provided with [RouteSettings]; in that configuration,
|
||||
/// the [navigatorKey], [onUnknownRoute], [navigatorObservers], and
|
||||
/// [initialRoute] properties must have their default values, as they will have no effect.
|
||||
///
|
||||
/// The `supportedLocales` argument must be a list of one or more elements.
|
||||
/// By default supportedLocales is `[const Locale('en', 'US')]`.
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This sample shows a basic Flutter application using [WidgetsApp].
|
||||
///
|
||||
/// ** See code in examples/api/lib/widgets/app/widgets_app.widgets_app.0.dart **
|
||||
/// {@end-tool}
|
||||
WidgetsApp({ // can't be const because the asserts use methods on Iterable :-(
|
||||
super.key,
|
||||
this.navigatorKey,
|
||||
|
@ -530,8 +536,23 @@ class WidgetsApp extends StatefulWidget {
|
|||
/// The [PageRoute] generator callback used when the app is navigated to a
|
||||
/// named route.
|
||||
///
|
||||
/// A [PageRoute] represents the page in a [Navigator], so that it can
|
||||
/// correctly animate between pages, and to represent the "return value" of
|
||||
/// a route (e.g. which button a user selected in a modal dialog).
|
||||
///
|
||||
/// This callback can be used, for example, to specify that a [MaterialPageRoute]
|
||||
/// or a [CupertinoPageRoute] should be used for building page transitions.
|
||||
///
|
||||
/// The [PageRouteFactory] type is generic, meaning the provided function must
|
||||
/// itself be generic. For example (with special emphasis on the `<T>` at the
|
||||
/// start of the closure):
|
||||
///
|
||||
/// ```dart
|
||||
/// pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) => PageRouteBuilder<T>(
|
||||
/// settings: settings,
|
||||
/// pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => builder(context),
|
||||
/// ),
|
||||
/// ```
|
||||
final PageRouteFactory? pageRouteBuilder;
|
||||
|
||||
/// {@template flutter.widgets.widgetsApp.routeInformationParser}
|
||||
|
|
|
@ -5657,9 +5657,17 @@ class RichText extends MultiChildRenderObjectWidget {
|
|||
final ui.TextHeightBehavior? textHeightBehavior;
|
||||
|
||||
/// The [SelectionRegistrar] this rich text is subscribed to.
|
||||
///
|
||||
/// If this is set, [selectionColor] must be non-null.
|
||||
final SelectionRegistrar? selectionRegistrar;
|
||||
|
||||
/// The color to use when painting the selection.
|
||||
///
|
||||
/// This is ignored if [selectionRegistrar] is null.
|
||||
///
|
||||
/// See the section on selections in the [RichText] top-level API
|
||||
/// documentation for more details on enabling selection in [RichText]
|
||||
/// widgets.
|
||||
final Color? selectionColor;
|
||||
|
||||
@override
|
||||
|
|
|
@ -252,6 +252,10 @@ bool debugItemsHaveDuplicateKeys(Iterable<Widget> items) {
|
|||
/// assert(debugCheckHasTable(context));
|
||||
/// ```
|
||||
///
|
||||
/// Always place this before any early returns, so that the invariant is checked
|
||||
/// in all cases. This prevents bugs from hiding until a particular codepath is
|
||||
/// hit.
|
||||
///
|
||||
/// This method can be expensive (it walks the element tree).
|
||||
///
|
||||
/// Does nothing if asserts are disabled. Always returns true.
|
||||
|
@ -282,6 +286,10 @@ bool debugCheckHasTable(BuildContext context) {
|
|||
/// assert(debugCheckHasMediaQuery(context));
|
||||
/// ```
|
||||
///
|
||||
/// Always place this before any early returns, so that the invariant is checked
|
||||
/// in all cases. This prevents bugs from hiding until a particular codepath is
|
||||
/// hit.
|
||||
///
|
||||
/// Does nothing if asserts are disabled. Always returns true.
|
||||
bool debugCheckHasMediaQuery(BuildContext context) {
|
||||
assert(() {
|
||||
|
@ -334,6 +342,10 @@ bool debugCheckHasMediaQuery(BuildContext context) {
|
|||
/// If they are non-null, they are included in the order above, interspersed
|
||||
/// with the more generic advice regarding [Directionality].
|
||||
///
|
||||
/// Always place this before any early returns, so that the invariant is checked
|
||||
/// in all cases. This prevents bugs from hiding until a particular codepath is
|
||||
/// hit.
|
||||
///
|
||||
/// Does nothing if asserts are disabled. Always returns true.
|
||||
bool debugCheckHasDirectionality(BuildContext context, { String? why, String? hint, String? alternative }) {
|
||||
assert(() {
|
||||
|
@ -406,6 +418,10 @@ void debugWidgetBuilderValue(Widget widget, Widget? built) {
|
|||
/// assert(debugCheckHasWidgetsLocalizations(context));
|
||||
/// ```
|
||||
///
|
||||
/// Always place this before any early returns, so that the invariant is checked
|
||||
/// in all cases. This prevents bugs from hiding until a particular codepath is
|
||||
/// hit.
|
||||
///
|
||||
/// Does nothing if asserts are disabled. Always returns true.
|
||||
bool debugCheckHasWidgetsLocalizations(BuildContext context) {
|
||||
assert(() {
|
||||
|
@ -443,6 +459,10 @@ bool debugCheckHasWidgetsLocalizations(BuildContext context) {
|
|||
/// assert(debugCheckHasOverlay(context));
|
||||
/// ```
|
||||
///
|
||||
/// Always place this before any early returns, so that the invariant is checked
|
||||
/// in all cases. This prevents bugs from hiding until a particular codepath is
|
||||
/// hit.
|
||||
///
|
||||
/// This method can be expensive (it walks the element tree).
|
||||
///
|
||||
/// Does nothing if asserts are disabled. Always returns true.
|
||||
|
|
|
@ -31,7 +31,7 @@ class DefaultSelectionStyle extends InheritedTheme {
|
|||
});
|
||||
|
||||
/// A const-constructable default selection style that provides fallback
|
||||
/// values.
|
||||
/// values (null).
|
||||
///
|
||||
/// Returned from [of] when the given [BuildContext] doesn't have an enclosing
|
||||
/// default selection style.
|
||||
|
@ -43,6 +43,12 @@ class DefaultSelectionStyle extends InheritedTheme {
|
|||
selectionColor = null,
|
||||
super(child: const _NullWidget());
|
||||
|
||||
/// The default cursor and selection color (semi-transparent grey).
|
||||
///
|
||||
/// This is the color that the [Text] widget uses when the specified selection
|
||||
/// color is null.
|
||||
static const Color defaultColor = Color(0x80808080);
|
||||
|
||||
/// The color of the text field's cursor.
|
||||
///
|
||||
/// The cursor indicates the current location of the text insertion point in
|
||||
|
@ -88,9 +94,9 @@ class _NullWidget extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
throw FlutterError(
|
||||
'A DefaultTextStyle constructed with DefaultTextStyle.fallback cannot be incorporated into the widget tree, '
|
||||
'it is meant only to provide a fallback value returned by DefaultTextStyle.of() '
|
||||
'when no enclosing default text style is present in a BuildContext.',
|
||||
'A DefaultSelectionStyle constructed with DefaultSelectionStyle.fallback cannot be incorporated into the widget tree, '
|
||||
'it is meant only to provide a fallback value returned by DefaultSelectionStyle.of() '
|
||||
'when no enclosing default selection style is present in a BuildContext.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -281,6 +281,10 @@ class _OverlayEntryWidgetState extends State<_OverlayEntryWidget> {
|
|||
/// navigation and being able to insert widgets on top of the pages in an app.
|
||||
/// To simply display a stack of widgets, consider using [Stack] instead.
|
||||
///
|
||||
/// An [Overlay] widget requires a [Directionality] widget to be in scope, so
|
||||
/// that it can resolve direction-sensitive coordinates of any
|
||||
/// [Positioned.directional] children.
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This example shows how to use the [Overlay] to highlight the [NavigationBar]
|
||||
/// destination.
|
||||
|
|
|
@ -7,6 +7,13 @@ import 'framework.dart';
|
|||
import 'routes.dart';
|
||||
|
||||
/// A modal route that replaces the entire screen.
|
||||
///
|
||||
/// The [PageRouteBuilder] subclass provides a way to create a [PageRoute] using
|
||||
/// callbacks rather than by defining a new class via subclassing.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Route], which documents the meaning of the `T` generic type argument.
|
||||
abstract class PageRoute<T> extends ModalRoute<T> {
|
||||
/// Creates a modal route that replaces the entire screen.
|
||||
PageRoute({
|
||||
|
@ -49,6 +56,13 @@ Widget _defaultTransitionsBuilder(BuildContext context, Animation<double> animat
|
|||
///
|
||||
/// Callers must define the [pageBuilder] function which creates the route's
|
||||
/// primary contents. To add transitions define the [transitionsBuilder] function.
|
||||
///
|
||||
/// The `T` generic type argument corresponds to the type argument of the
|
||||
/// created [Route] objects.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Route], which documents the meaning of the `T` generic type argument.
|
||||
class PageRouteBuilder<T> extends PageRoute<T> {
|
||||
/// Creates a route that delegates to builder callbacks.
|
||||
///
|
||||
|
@ -86,6 +100,8 @@ class PageRouteBuilder<T> extends PageRoute<T> {
|
|||
///
|
||||
/// See [ModalRoute.buildTransitions] for complete definition of the parameters.
|
||||
/// {@endtemplate}
|
||||
///
|
||||
/// The default transition is a jump cut (i.e. no animation).
|
||||
final RouteTransitionsBuilder transitionsBuilder;
|
||||
|
||||
@override
|
||||
|
|
|
@ -34,6 +34,10 @@ import 'transitions.dart';
|
|||
// late StateSetter setState;
|
||||
|
||||
/// A route that displays widgets in the [Navigator]'s [Overlay].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Route], which documents the meaning of the `T` generic type argument.
|
||||
abstract class OverlayRoute<T> extends Route<T> {
|
||||
/// Creates a route that knows how to interact with an [Overlay].
|
||||
OverlayRoute({
|
||||
|
@ -85,6 +89,10 @@ abstract class OverlayRoute<T> extends Route<T> {
|
|||
}
|
||||
|
||||
/// A route with entrance and exit transitions.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Route], which documents the meaning of the `T` generic type argument.
|
||||
abstract class TransitionRoute<T> extends OverlayRoute<T> {
|
||||
/// Creates a route that animates itself when it is pushed or popped.
|
||||
TransitionRoute({
|
||||
|
@ -507,6 +515,10 @@ class LocalHistoryEntry {
|
|||
/// pop internally if its list of local history entries is non-empty. Rather
|
||||
/// than being removed as the current route, the most recent [LocalHistoryEntry]
|
||||
/// is removed from the list and its [LocalHistoryEntry.onRemove] is called.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Route], which documents the meaning of the `T` generic type argument.
|
||||
mixin LocalHistoryRoute<T> on Route<T> {
|
||||
List<LocalHistoryEntry>? _localHistory;
|
||||
int _entriesImpliesAppBarDismissal = 0;
|
||||
|
@ -946,6 +958,10 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
|
|||
///
|
||||
/// The `T` type argument is the return value of the route. If there is no
|
||||
/// return value, consider using `void` as the return value.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Route], which further documents the meaning of the `T` generic type argument.
|
||||
abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T> {
|
||||
/// Creates a route that blocks interaction with previous routes.
|
||||
ModalRoute({
|
||||
|
|
|
@ -26,6 +26,10 @@ import 'selection_container.dart';
|
|||
import 'text_editing_intents.dart';
|
||||
import 'text_selection.dart';
|
||||
|
||||
// Examples can assume:
|
||||
// FocusNode _focusNode = FocusNode();
|
||||
// late GlobalKey key;
|
||||
|
||||
const Set<PointerDeviceKind> _kLongPressSelectionDevices = <PointerDeviceKind>{
|
||||
PointerDeviceKind.touch,
|
||||
PointerDeviceKind.stylus,
|
||||
|
@ -34,13 +38,22 @@ const Set<PointerDeviceKind> _kLongPressSelectionDevices = <PointerDeviceKind>{
|
|||
|
||||
/// A widget that introduces an area for user selections.
|
||||
///
|
||||
/// Flutter widgets are not selectable by default. To enable selection for
|
||||
/// a Flutter application, consider wrapping a portion of widget subtree with
|
||||
/// [SelectableRegion]. The wrapped subtree can be selected by users using mouse
|
||||
/// or touch gestures, e.g. users can select widgets by holding the mouse
|
||||
/// Flutter widgets are not selectable by default. Wrapping a widget subtree
|
||||
/// with a [SelectableRegion] widget enables selection within that subtree (for
|
||||
/// example, [Text] widgets automatically look for selectable regions to enable
|
||||
/// selection). The wrapped subtree can be selected by users using mouse or
|
||||
/// touch gestures, e.g. users can select widgets by holding the mouse
|
||||
/// left-click and dragging across widgets, or they can use long press gestures
|
||||
/// to select words on touch devices.
|
||||
///
|
||||
/// A [SelectableRegion] widget requires configuration; in particular specific
|
||||
/// [selectionControls] must be provided.
|
||||
///
|
||||
/// The [SelectionArea] widget from the [material] library configures a
|
||||
/// [SelectableRegion] in a platform-specific manner (e.g. using a Material
|
||||
/// toolbar on Android, a Cupertino toolbar on iOS), and it may therefore be
|
||||
/// simpler to use that widget rather than using [SelectableRegion] directly.
|
||||
///
|
||||
/// ## An overview of the selection system.
|
||||
///
|
||||
/// Every [Selectable] under the [SelectableRegion] can be selected. They form a
|
||||
|
@ -77,7 +90,7 @@ const Set<PointerDeviceKind> _kLongPressSelectionDevices = <PointerDeviceKind>{
|
|||
/// MaterialApp(
|
||||
/// home: SelectableRegion(
|
||||
/// selectionControls: materialTextSelectionControls,
|
||||
/// focusNode: FocusNode(),
|
||||
/// focusNode: _focusNode, // initialized to FocusNode()
|
||||
/// child: Scaffold(
|
||||
/// appBar: AppBar(title: const Text('Flutter Code Sample')),
|
||||
/// body: ListView(
|
||||
|
@ -160,6 +173,16 @@ const Set<PointerDeviceKind> _kLongPressSelectionDevices = <PointerDeviceKind>{
|
|||
/// child selection area can not extend past its subtree, and the selection of
|
||||
/// the parent selection area can not extend inside the child selection area.
|
||||
///
|
||||
/// ## Tests
|
||||
///
|
||||
/// In a test, a region can be selected either by faking drag events (e.g. using
|
||||
/// [WidgetTester.dragFrom]) or by sending intents to a widget inside the region
|
||||
/// that has been given a [GlobalKey], e.g.:
|
||||
///
|
||||
/// ```dart
|
||||
/// Actions.invoke(key.currentContext!, const SelectAllTextIntent(SelectionChangedCause.keyboard));
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
/// * [SelectionArea], which creates a [SelectableRegion] with
|
||||
/// platform-adaptive selection controls.
|
||||
|
@ -202,6 +225,9 @@ class SelectableRegion extends StatefulWidget {
|
|||
|
||||
/// The delegate to build the selection handles and toolbar for mobile
|
||||
/// devices.
|
||||
///
|
||||
/// The [emptyTextSelectionControls] global variable provides a default
|
||||
/// [TextSelectionControls] implementation with no controls.
|
||||
final TextSelectionControls selectionControls;
|
||||
|
||||
@override
|
||||
|
|
|
@ -14,7 +14,8 @@ import 'framework.dart';
|
|||
///
|
||||
/// The state of this container is a single selectable and will register
|
||||
/// itself to the [registrar] if provided. Otherwise, it will register to the
|
||||
/// [SelectionRegistrar] from the context.
|
||||
/// [SelectionRegistrar] from the context. Consider using a [SelectionArea]
|
||||
/// widget to provide a root registrar.
|
||||
///
|
||||
/// The containers handle the [SelectionEvent]s from the registered
|
||||
/// [SelectionRegistrar] and delegate the events to the [delegate].
|
||||
|
|
|
@ -533,6 +533,13 @@ class Text extends StatelessWidget {
|
|||
final ui.TextHeightBehavior? textHeightBehavior;
|
||||
|
||||
/// The color to use when painting the selection.
|
||||
///
|
||||
/// This is ignored if [SelectionContainer.maybeOf] returns null
|
||||
/// in the [BuildContext] of the [Text] widget.
|
||||
///
|
||||
/// If null, the ambient [DefaultSelectionStyle] is used (if any); failing
|
||||
/// that, the selection color defaults to [DefaultSelectionStyle.defaultColor]
|
||||
/// (semi-transparent grey).
|
||||
final Color? selectionColor;
|
||||
|
||||
@override
|
||||
|
@ -558,7 +565,7 @@ class Text extends StatelessWidget {
|
|||
textWidthBasis: textWidthBasis ?? defaultTextStyle.textWidthBasis,
|
||||
textHeightBehavior: textHeightBehavior ?? defaultTextStyle.textHeightBehavior ?? DefaultTextHeightBehavior.of(context),
|
||||
selectionRegistrar: registrar,
|
||||
selectionColor: selectionColor ?? DefaultSelectionStyle.of(context).selectionColor,
|
||||
selectionColor: selectionColor ?? DefaultSelectionStyle.of(context).selectionColor ?? DefaultSelectionStyle.defaultColor,
|
||||
text: TextSpan(
|
||||
style: effectiveTextStyle,
|
||||
text: data,
|
||||
|
|
|
@ -26,6 +26,7 @@ import 'tap_region.dart';
|
|||
import 'ticker_provider.dart';
|
||||
import 'transitions.dart';
|
||||
|
||||
export 'package:flutter/rendering.dart' show TextSelectionPoint;
|
||||
export 'package:flutter/services.dart' show TextSelectionDelegate;
|
||||
|
||||
/// A duration that controls how often the drag selection update callback is
|
||||
|
@ -76,6 +77,11 @@ class ToolbarItemsParentData extends ContainerBoxParentData<RenderBox> {
|
|||
/// implementer of the toolbar widget.
|
||||
///
|
||||
/// Override text operations such as [handleCut] if needed.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SelectionArea], which selects appropriate text selection controls
|
||||
/// based on the current platform.
|
||||
abstract class TextSelectionControls {
|
||||
/// Builds a selection handle of the given `type`.
|
||||
///
|
||||
|
@ -97,20 +103,20 @@ abstract class TextSelectionControls {
|
|||
///
|
||||
/// Typically displays buttons for copying and pasting text.
|
||||
///
|
||||
/// [globalEditableRegion] is the TextField size of the global coordinate system
|
||||
/// in logical pixels.
|
||||
/// The [globalEditableRegion] parameter is the TextField size of the global
|
||||
/// coordinate system in logical pixels.
|
||||
///
|
||||
/// [textLineHeight] is the `preferredLineHeight` of the [RenderEditable] we
|
||||
/// are building a toolbar for.
|
||||
/// The [textLineHeight] parameter is the [RenderEditable.preferredLineHeight]
|
||||
/// of the [RenderEditable] we are building a toolbar for.
|
||||
///
|
||||
/// The [position] is a general calculation midpoint parameter of the toolbar.
|
||||
/// If you want more detailed position information, can use [endpoints]
|
||||
/// to calculate it.
|
||||
/// The [selectionMidpoint] parameter is a general calculation midpoint
|
||||
/// parameter of the toolbar. More detailed position information
|
||||
/// is computable from the [endpoints] parameter.
|
||||
Widget buildToolbar(
|
||||
BuildContext context,
|
||||
Rect globalEditableRegion,
|
||||
double textLineHeight,
|
||||
Offset position,
|
||||
Offset selectionMidpoint,
|
||||
List<TextSelectionPoint> endpoints,
|
||||
TextSelectionDelegate delegate,
|
||||
ValueListenable<ClipboardStatus>? clipboardStatus,
|
||||
|
@ -207,6 +213,55 @@ abstract class TextSelectionControls {
|
|||
}
|
||||
}
|
||||
|
||||
/// Text selection controls that do not show any toolbars or handles.
|
||||
///
|
||||
/// This is a placeholder, suitable for temporary use during development, but
|
||||
/// not practical for production. For example, it provides no way for the user
|
||||
/// to interact with selections: no context menus on desktop, no toolbars or
|
||||
/// drag handles on mobile, etc. For production, consider using
|
||||
/// [MaterialTextSelectionControls] or creating a custom subclass of
|
||||
/// [TextSelectionControls].
|
||||
///
|
||||
/// The [emptyTextSelectionControls] global variable has a
|
||||
/// suitable instance of this class.
|
||||
class EmptyTextSelectionControls extends TextSelectionControls {
|
||||
@override
|
||||
Size getHandleSize(double textLineHeight) => Size.zero;
|
||||
|
||||
@override
|
||||
Widget buildToolbar(
|
||||
BuildContext context,
|
||||
Rect globalEditableRegion,
|
||||
double textLineHeight,
|
||||
Offset selectionMidpoint,
|
||||
List<TextSelectionPoint> endpoints,
|
||||
TextSelectionDelegate delegate,
|
||||
ValueListenable<ClipboardStatus>? clipboardStatus,
|
||||
Offset? lastSecondaryTapDownPosition,
|
||||
) => const SizedBox.shrink();
|
||||
|
||||
@override
|
||||
Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textLineHeight, [VoidCallback? onTap]) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
@override
|
||||
Offset getHandleAnchor(TextSelectionHandleType type, double textLineHeight) {
|
||||
return Offset.zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// Text selection controls that do not show any toolbars or handles.
|
||||
///
|
||||
/// This is a placeholder, suitable for temporary use during development, but
|
||||
/// not practical for production. For example, it provides no way for the user
|
||||
/// to interact with selections: no context menus on desktop, no toolbars or
|
||||
/// drag handles on mobile, etc. For production, consider using
|
||||
/// [materialTextSelectionControls] or creating a custom subclass of
|
||||
/// [TextSelectionControls].
|
||||
final TextSelectionControls emptyTextSelectionControls = EmptyTextSelectionControls();
|
||||
|
||||
|
||||
/// An object that manages a pair of text selection handles for a
|
||||
/// [RenderEditable].
|
||||
///
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../widgets/editable_text_utils.dart' show textOffsetToPosition;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../widgets/editable_text_utils.dart' show textOffsetToPosition;
|
||||
|
|
153
packages/flutter/test/widgets/default_colors_test.dart
Normal file
153
packages/flutter/test/widgets/default_colors_test.dart
Normal file
|
@ -0,0 +1,153 @@
|
|||
// Copyright 2014 The Flutter 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 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
const double _crispText = 100.0; // this font size is selected to avoid needing any antialiasing.
|
||||
const String _expText = 'Éxp'; // renders in Ahem as:
|
||||
|
||||
// ########
|
||||
// ########
|
||||
// ########
|
||||
// ########
|
||||
//
|
||||
// ÉÉÉÉxxxxpppp
|
||||
|
||||
void main() {
|
||||
testWidgets('Default background', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text(_expText, textDirection: TextDirection.ltr, style: TextStyle(color: Color(0xFF345678), fontSize: _crispText))),
|
||||
);
|
||||
await _expectColors(
|
||||
tester,
|
||||
find.byType(Align),
|
||||
<Color>{ const Color(0x00000000), const Color(0xFF345678) },
|
||||
<Offset, Color>{
|
||||
Offset.zero: const Color(0xFF345678), // the text
|
||||
const Offset(10, 10): const Color(0xFF345678), // the text
|
||||
const Offset(50, 95): const Color(0x00000000), // the background (under the É)
|
||||
const Offset(250, 50): const Color(0x00000000), // the text (above the p)
|
||||
const Offset(250, 95): const Color(0xFF345678), // the text (the p)
|
||||
const Offset(400, 400): const Color(0x00000000), // the background
|
||||
const Offset(799, 599): const Color(0x00000000), // the background
|
||||
},
|
||||
);
|
||||
}, skip: !canCaptureImage); // [intended] Test relies on captureImage, which is not supported on web currently.
|
||||
|
||||
testWidgets('Default text color', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const ColoredBox(
|
||||
color: Color(0xFFABCDEF),
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text('Éxp', textDirection: TextDirection.ltr, style: TextStyle(fontSize: _crispText)),
|
||||
),
|
||||
),
|
||||
);
|
||||
await _expectColors(
|
||||
tester,
|
||||
find.byType(Align),
|
||||
<Color>{ const Color(0xFFABCDEF), const Color(0xFFFFFFFF) },
|
||||
<Offset, Color>{
|
||||
Offset.zero: const Color(0xFFFFFFFF), // the text
|
||||
const Offset(10, 10): const Color(0xFFFFFFFF), // the text
|
||||
const Offset(50, 95): const Color(0xFFABCDEF), // the background (under the É)
|
||||
const Offset(250, 50): const Color(0xFFABCDEF), // the text (above the p)
|
||||
const Offset(250, 95): const Color(0xFFFFFFFF), // the text (the p)
|
||||
const Offset(400, 400): const Color(0xFFABCDEF), // the background
|
||||
const Offset(799, 599): const Color(0xFFABCDEF), // the background
|
||||
},
|
||||
);
|
||||
}, skip: !canCaptureImage); // [intended] Test relies on captureImage, which is not supported on web currently.
|
||||
|
||||
testWidgets('Default text selection color', (WidgetTester tester) async {
|
||||
final GlobalKey key = GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
ColoredBox(
|
||||
color: const Color(0xFFFFFFFF),
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: MediaQuery(
|
||||
data: const MediaQueryData(),
|
||||
child: Overlay(
|
||||
initialEntries: <OverlayEntry>[
|
||||
OverlayEntry(
|
||||
builder: (BuildContext context) => SelectableRegion(
|
||||
focusNode: FocusNode(),
|
||||
selectionControls: emptyTextSelectionControls,
|
||||
child: Align(
|
||||
key: key,
|
||||
alignment: Alignment.topLeft,
|
||||
child: const Text('Éxp', textDirection: TextDirection.ltr, style: TextStyle(fontSize: _crispText)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await _expectColors(
|
||||
tester,
|
||||
find.byType(Align),
|
||||
<Color>{ const Color(0xFFFFFFFF) },
|
||||
);
|
||||
// fake a "select all" event to selecte the text
|
||||
Actions.invoke(key.currentContext!, const SelectAllTextIntent(SelectionChangedCause.keyboard));
|
||||
await tester.pump();
|
||||
await _expectColors(
|
||||
tester,
|
||||
find.byType(Align),
|
||||
<Color>{ const Color(0xFFFFFFFF), const Color(0xFFBFBFBF) }, // 0x80808080 blended with 0xFFFFFFFF
|
||||
<Offset, Color>{
|
||||
Offset.zero: const Color(0xFFBFBFBF), // the selected text
|
||||
const Offset(10, 10): const Color(0xFFBFBFBF), // the selected text
|
||||
const Offset(50, 95): const Color(0xFFBFBFBF), // the selected background (under the É)
|
||||
const Offset(250, 50): const Color(0xFFBFBFBF), // the selected background (above the p)
|
||||
const Offset(250, 95): const Color(0xFFBFBFBF), // the selected text (the p)
|
||||
const Offset(400, 400): const Color(0xFFFFFFFF), // the background
|
||||
const Offset(799, 599): const Color(0xFFFFFFFF), // the background
|
||||
},
|
||||
);
|
||||
}, skip: !canCaptureImage); // [intended] Test relies on captureImage, which is not supported on web currently.
|
||||
}
|
||||
|
||||
Color _getPixel(ByteData bytes, int x, int y, int width) {
|
||||
final int offset = (x + y * width) * 4;
|
||||
return Color.fromARGB(
|
||||
bytes.getUint8(offset + 3),
|
||||
bytes.getUint8(offset + 0),
|
||||
bytes.getUint8(offset + 1),
|
||||
bytes.getUint8(offset + 2),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _expectColors(WidgetTester tester, Finder finder, Set<Color> allowedColors, [ Map<Offset, Color>? spotChecks ]) async {
|
||||
final TestWidgetsFlutterBinding binding = tester.binding;
|
||||
final ui.Image image = (await binding.runAsync<ui.Image>(() => captureImage(finder.evaluate().single)))!;
|
||||
final ByteData bytes = (await binding.runAsync<ByteData?>(() => image.toByteData(format: ui.ImageByteFormat.rawStraightRgba)))!;
|
||||
final Set<int> actualColorValues = <int>{};
|
||||
for (int offset = 0; offset < bytes.lengthInBytes; offset += 4) {
|
||||
actualColorValues.add((bytes.getUint8(offset + 3) << 24) +
|
||||
(bytes.getUint8(offset + 0) << 16) +
|
||||
(bytes.getUint8(offset + 1) << 8) +
|
||||
(bytes.getUint8(offset + 2)));
|
||||
}
|
||||
final Set<Color> actualColors = actualColorValues.map((int value) => Color(value)).toSet();
|
||||
expect(actualColors, allowedColors);
|
||||
spotChecks?.forEach((Offset position, Color expected) {
|
||||
assert(position.dx.round() >= 0);
|
||||
assert(position.dx.round() < image.width);
|
||||
assert(position.dy.round() >= 0);
|
||||
assert(position.dy.round() < image.height);
|
||||
final Offset precisePosition = position * binding.window.devicePixelRatio;
|
||||
final Color actual = _getPixel(bytes, precisePosition.dx.round(), precisePosition.dy.round(), image.width);
|
||||
expect(actual, expected, reason: 'Pixel at $position is $actual but expected $expected.');
|
||||
});
|
||||
}
|
|
@ -56,6 +56,7 @@ library flutter_test;
|
|||
export 'dart:async' show Future;
|
||||
|
||||
export 'src/_goldens_io.dart' if (dart.library.html) 'src/_goldens_web.dart';
|
||||
export 'src/_matchers_io.dart' if (dart.library.html) 'src/_matchers_web.dart';
|
||||
export 'src/accessibility.dart';
|
||||
export 'src/all_elements.dart';
|
||||
export 'src/animation_sheet.dart';
|
||||
|
|
|
@ -30,6 +30,15 @@ Future<ui.Image> captureImage(Element element) {
|
|||
return layer.toImage(renderObject.paintBounds);
|
||||
}
|
||||
|
||||
/// Whether or not [captureImage] is supported.
|
||||
///
|
||||
/// This can be used to skip tests on platforms that don't support
|
||||
/// capturing images.
|
||||
///
|
||||
/// Currently this is true except when tests are running in the context of a web
|
||||
/// browser (`flutter test --platform chrome`).
|
||||
const bool canCaptureImage = true;
|
||||
|
||||
/// The matcher created by [matchesGoldenFile]. This class is enabled when the
|
||||
/// test is running on a VM using conditional import.
|
||||
class MatchesGoldenFile extends AsyncMatcher {
|
||||
|
@ -84,7 +93,7 @@ class MatchesGoldenFile extends AsyncMatcher {
|
|||
throw AssertionError('must provide a Finder, Image, Future<Image>, List<int>, or Future<List<int>>');
|
||||
}
|
||||
|
||||
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.instance;
|
||||
return binding.runAsync<String?>(() async {
|
||||
final ui.Image? image = await imageFuture;
|
||||
if (image == null) {
|
||||
|
|
|
@ -18,6 +18,15 @@ Future<ui.Image> captureImage(Element element) {
|
|||
throw UnsupportedError('captureImage is not supported on the web.');
|
||||
}
|
||||
|
||||
/// Whether or not [captureImage] is supported.
|
||||
///
|
||||
/// This can be used to skip tests on platforms that don't support
|
||||
/// capturing images.
|
||||
///
|
||||
/// Currently this is true except when tests are running in the context of a web
|
||||
/// browser (`flutter test --platform chrome`).
|
||||
const bool canCaptureImage = false;
|
||||
|
||||
/// The matcher created by [matchesGoldenFile]. This class is enabled when the
|
||||
/// test is running in a web browser using conditional import.
|
||||
class MatchesGoldenFile extends AsyncMatcher {
|
||||
|
@ -47,7 +56,7 @@ class MatchesGoldenFile extends AsyncMatcher {
|
|||
final Element element = elements.single;
|
||||
final RenderObject renderObject = _findRepaintBoundary(element);
|
||||
final Size size = renderObject.paintBounds.size;
|
||||
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.instance;
|
||||
final Element e = binding.renderViewElement!;
|
||||
|
||||
// Unlike `flutter_tester`, we don't have the ability to render an element
|
||||
|
|
|
@ -1131,28 +1131,45 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
|||
|
||||
addTime(additionalTime);
|
||||
|
||||
return realAsyncZone.run<Future<T?>>(() async {
|
||||
return realAsyncZone.run<Future<T?>>(() {
|
||||
final Completer<T?> result = Completer<T?>();
|
||||
_pendingAsyncTasks = Completer<void>();
|
||||
T? result;
|
||||
try {
|
||||
result = await callback();
|
||||
callback().then(result.complete).catchError(
|
||||
(Object exception, StackTrace stack) {
|
||||
FlutterError.reportError(FlutterErrorDetails(
|
||||
exception: exception,
|
||||
stack: stack,
|
||||
library: 'Flutter test framework',
|
||||
context: ErrorDescription('while running async test code'),
|
||||
informationCollector: () {
|
||||
return <DiagnosticsNode>[
|
||||
ErrorHint('The exception was caught asynchronously.'),
|
||||
];
|
||||
},
|
||||
));
|
||||
result.complete(null);
|
||||
},
|
||||
);
|
||||
} catch (exception, stack) {
|
||||
FlutterError.reportError(FlutterErrorDetails(
|
||||
exception: exception,
|
||||
stack: stack,
|
||||
library: 'Flutter test framework',
|
||||
context: ErrorDescription('while running async test code'),
|
||||
informationCollector: () {
|
||||
return <DiagnosticsNode>[
|
||||
ErrorHint('The exception was caught synchronously.'),
|
||||
];
|
||||
},
|
||||
));
|
||||
} finally {
|
||||
// We complete the _pendingAsyncTasks future successfully regardless of
|
||||
// whether an exception occurred because in the case of an exception,
|
||||
// we already reported the exception to FlutterError. Moreover,
|
||||
// completing the future with an error would trigger an unhandled
|
||||
// exception due to zone error boundaries.
|
||||
result.complete(null);
|
||||
}
|
||||
result.future.whenComplete(() {
|
||||
_pendingAsyncTasks!.complete();
|
||||
_pendingAsyncTasks = null;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
return result.future;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -60,11 +60,11 @@ class _BufferGoldenMatcher extends AsyncMatcher {
|
|||
/// golden files. This parameter is optional.
|
||||
///
|
||||
/// {@tool snippet}
|
||||
/// Sample invocations of [matchesGoldenFile].
|
||||
/// Sample invocations of [bufferMatchesGoldenFile].
|
||||
///
|
||||
/// ```dart
|
||||
/// await expectLater(
|
||||
/// const <int>[],
|
||||
/// const <int>[ /* bytes... */ ],
|
||||
/// bufferMatchesGoldenFile('sample.png'),
|
||||
/// );
|
||||
/// ```
|
||||
|
|
|
@ -2030,7 +2030,7 @@ class _MatchesReferenceImage extends AsyncMatcher {
|
|||
imageFuture = captureImage(elements.single);
|
||||
}
|
||||
|
||||
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.instance;
|
||||
return binding.runAsync<String?>(() async {
|
||||
final ui.Image image = await imageFuture;
|
||||
final ByteData? bytes = await image.toByteData();
|
||||
|
|
|
@ -630,6 +630,23 @@ void main() {
|
|||
key: 'abczed',
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('control test (return value)', (WidgetTester tester) async {
|
||||
final String? result = await tester.binding.runAsync<String>(() async => 'Judy Turner');
|
||||
expect(result, 'Judy Turner');
|
||||
});
|
||||
|
||||
testWidgets('async throw', (WidgetTester tester) async {
|
||||
final String? result = await tester.binding.runAsync<Never>(() async => throw Exception('Lois Dilettente'));
|
||||
expect(result, isNull);
|
||||
expect(tester.takeException(), isNotNull);
|
||||
});
|
||||
|
||||
testWidgets('sync throw', (WidgetTester tester) async {
|
||||
final String? result = await tester.binding.runAsync<Never>(() => throw Exception('Butch Barton'));
|
||||
expect(result, isNull);
|
||||
expect(tester.takeException(), isNotNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('showKeyboard', () {
|
||||
|
|
Loading…
Reference in a new issue