Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
4a7e30f8
Unverified
Commit
4a7e30f8
authored
Feb 02, 2022
by
Andrei Diaconu
Committed by
GitHub
Feb 02, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add DisplayFeatureSubScreen widget (#92907)
parent
ba4d63a4
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
680 additions
and
0 deletions
+680
-0
display_feature_sub_screen.dart
...s/flutter/lib/src/widgets/display_feature_sub_screen.dart
+255
-0
media_query.dart
packages/flutter/lib/src/widgets/media_query.dart
+49
-0
widgets.dart
packages/flutter/lib/widgets.dart
+1
-0
display_feature_sub_screen_test.dart
...flutter/test/widgets/display_feature_sub_screen_test.dart
+221
-0
media_query_test.dart
packages/flutter/test/widgets/media_query_test.dart
+154
-0
No files found.
packages/flutter/lib/src/widgets/display_feature_sub_screen.dart
0 → 100644
View file @
4a7e30f8
// 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:math'
as
math
;
import
'dart:ui'
show
DisplayFeature
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/rendering.dart'
;
import
'basic.dart'
;
import
'debug.dart'
;
import
'framework.dart'
;
import
'media_query.dart'
;
/// Positions [child] such that it avoids overlapping any [DisplayFeature] that
/// splits the screen into sub-screens.
///
/// A [DisplayFeature] splits the screen into sub-screens when both these
/// conditions are met:
///
/// * it obstructs the screen, meaning the area it occupies is not 0. Display
/// features of type [DisplayFeatureType.fold] can have height 0 or width 0
/// and not be obstructing the screen.
/// * it is at least as tall as the screen, producing a left and right
/// sub-screen or it is at least as wide as the screen, producing a top and
/// bottom sub-screen
///
/// After determining the sub-screens, the closest one to [anchorPoint] is used
/// to render the [child].
///
/// If no [anchorPoint] is provided, then [Directionality] is used:
///
/// * for [TextDirection.ltr], [anchorPoint] is `Offset.zero`, which will
/// cause the [child] to appear in the top-left sub-screen.
/// * for [TextDirection.rtl], [anchorPoint] is `Offset(double.maxFinite, 0)`,
/// which will cause the [child] to appear in the top-right sub-screen.
///
/// If no [anchorPoint] is provided, and there is no [Directionality] ancestor
/// widget in the tree, then the widget asserts during build in debug mode.
///
/// Similarly to [SafeArea], this widget assumes there is no added padding
/// between it and the first [MediaQuery] ancestor. The [child] is wrapped in a
/// new [MediaQuery] instance containing the [DisplayFeature]s that exist in the
/// selected sub-screen, with coordinates relative to the sub-screen. Padding is
/// also adjusted to zero out any sides that were avoided by this widget.
///
/// See also:
///
/// * [showDialog], which is a way to display a [DialogRoute].
/// * [showCupertinoDialog], which displays an iOS-style dialog.
class
DisplayFeatureSubScreen
extends
StatelessWidget
{
/// Creates a widget that positions its child so that it avoids display
/// features.
const
DisplayFeatureSubScreen
({
Key
?
key
,
this
.
anchorPoint
,
required
this
.
child
,
})
:
super
(
key:
key
);
/// The anchor point used to pick the closest sub-screen.
///
/// If the anchor point sits inside one of these sub-screens, then that
/// sub-screen is picked. If not, then the sub-screen with the closest edge to
/// the point is used.
///
/// [Offset.zero] is the top-left corner of the available screen space. For a
/// vertically split dual-screen device, this is the top-left corner of the
/// left screen.
///
/// When this is null, [Directionality] is used:
///
/// * for [TextDirection.ltr], [anchorPoint] is [Offset.zero], which will
/// cause the top-left sub-screen to be picked.
/// * for [TextDirection.rtl], [anchorPoint] is
/// `Offset(double.maxFinite, 0)`, which will cause the top-right
/// sub-screen to be picked.
final
Offset
?
anchorPoint
;
/// The widget below this widget in the tree.
///
/// The padding on the [MediaQuery] for the [child] will be suitably adjusted
/// to zero out any sides that were avoided by this widget. The [MediaQuery]
/// for the [child] will no longer contain any display features that split the
/// screen into sub-screens.
///
/// {@macro flutter.widgets.ProxyWidget.child}
final
Widget
child
;
@override
Widget
build
(
BuildContext
context
)
{
assert
(
anchorPoint
!=
null
||
debugCheckHasDirectionality
(
context
,
why:
'to determine which sub-screen DisplayFeatureSubScreen uses'
,
alternative:
"Alternatively, consider specifying the 'anchorPoint' argument on the DisplayFeatureSubScreen."
,
));
final
MediaQueryData
mediaQuery
=
MediaQuery
.
of
(
context
);
final
Size
parentSize
=
mediaQuery
.
size
;
final
Rect
wantedBounds
=
Offset
.
zero
&
parentSize
;
final
Offset
resolvedAnchorPoint
=
_capOffset
(
anchorPoint
??
_fallbackAnchorPoint
(
context
),
parentSize
);
final
Iterable
<
Rect
>
subScreens
=
_subScreensInBounds
(
wantedBounds
,
_avoidBounds
(
mediaQuery
));
final
Rect
closestSubScreen
=
_closestToAnchorPoint
(
subScreens
,
resolvedAnchorPoint
);
return
Padding
(
padding:
EdgeInsets
.
only
(
left:
closestSubScreen
.
left
,
top:
closestSubScreen
.
top
,
right:
parentSize
.
width
-
closestSubScreen
.
right
,
bottom:
parentSize
.
height
-
closestSubScreen
.
bottom
,
),
child:
MediaQuery
(
data:
mediaQuery
.
removeDisplayFeatures
(
closestSubScreen
),
child:
child
,
),
);
}
static
Offset
_fallbackAnchorPoint
(
BuildContext
context
)
{
final
TextDirection
textDirection
=
Directionality
.
of
(
context
);
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
return
const
Offset
(
double
.
maxFinite
,
0
);
case
TextDirection
.
ltr
:
return
Offset
.
zero
;
}
}
static
Iterable
<
Rect
>
_avoidBounds
(
MediaQueryData
mediaQuery
)
{
return
mediaQuery
.
displayFeatures
.
map
((
DisplayFeature
d
)
=>
d
.
bounds
)
.
where
((
Rect
r
)
=>
r
.
shortestSide
>
0
);
}
/// Returns the closest sub-screen to the [anchorPoint].
static
Rect
_closestToAnchorPoint
(
Iterable
<
Rect
>
subScreens
,
Offset
anchorPoint
)
{
Rect
closestScreen
=
subScreens
.
first
;
double
closestDistance
=
_distanceFromPointToRect
(
anchorPoint
,
closestScreen
);
for
(
final
Rect
screen
in
subScreens
)
{
final
double
subScreenDistance
=
_distanceFromPointToRect
(
anchorPoint
,
screen
);
if
(
subScreenDistance
<
closestDistance
)
{
closestScreen
=
screen
;
closestDistance
=
subScreenDistance
;
}
}
return
closestScreen
;
}
static
double
_distanceFromPointToRect
(
Offset
point
,
Rect
rect
)
{
// Cases for point position relative to rect:
// 1 2 3
// 4 [R] 5
// 6 7 8
if
(
point
.
dx
<
rect
.
left
)
{
if
(
point
.
dy
<
rect
.
top
)
{
// Case 1
return
(
point
-
rect
.
topLeft
).
distance
;
}
else
if
(
point
.
dy
>
rect
.
bottom
)
{
// Case 6
return
(
point
-
rect
.
bottomLeft
).
distance
;
}
else
{
// Case 4
return
rect
.
left
-
point
.
dx
;
}
}
else
if
(
point
.
dx
>
rect
.
right
)
{
if
(
point
.
dy
<
rect
.
top
)
{
// Case 3
return
(
point
-
rect
.
topRight
).
distance
;
}
else
if
(
point
.
dy
>
rect
.
bottom
)
{
// Case 8
return
(
point
-
rect
.
bottomRight
).
distance
;
}
else
{
// Case 5
return
point
.
dx
-
rect
.
right
;
}
}
else
{
if
(
point
.
dy
<
rect
.
top
)
{
// Case 2
return
rect
.
top
-
point
.
dy
;
}
else
if
(
point
.
dy
>
rect
.
bottom
)
{
// Case 7
return
point
.
dy
-
rect
.
bottom
;
}
else
{
// Case R
return
0
;
}
}
}
/// Returns sub-screens resulted by dividing [wantedBounds] along items of
/// [avoidBounds] that are at least as high or as wide.
static
Iterable
<
Rect
>
_subScreensInBounds
(
Rect
wantedBounds
,
Iterable
<
Rect
>
avoidBounds
)
{
Iterable
<
Rect
>
subScreens
=
<
Rect
>[
wantedBounds
];
for
(
final
Rect
bounds
in
avoidBounds
)
{
final
List
<
Rect
>
newSubScreens
=
<
Rect
>[];
for
(
final
Rect
screen
in
subScreens
)
{
if
(
screen
.
top
>=
bounds
.
top
&&
screen
.
bottom
<=
bounds
.
bottom
)
{
// Display feature splits the screen vertically
if
(
screen
.
left
<
bounds
.
left
)
{
// There is a smaller sub-screen, left of the display feature
newSubScreens
.
add
(
Rect
.
fromLTWH
(
screen
.
left
,
screen
.
top
,
bounds
.
left
-
screen
.
left
,
screen
.
height
,
));
}
if
(
screen
.
right
>
bounds
.
right
)
{
// There is a smaller sub-screen, right of the display feature
newSubScreens
.
add
(
Rect
.
fromLTWH
(
bounds
.
right
,
screen
.
top
,
screen
.
right
-
bounds
.
right
,
screen
.
height
,
));
}
}
else
if
(
screen
.
left
>=
bounds
.
left
&&
screen
.
right
<=
bounds
.
right
)
{
// Display feature splits the sub-screen horizontally
if
(
screen
.
top
<
bounds
.
top
)
{
// There is a smaller sub-screen, above the display feature
newSubScreens
.
add
(
Rect
.
fromLTWH
(
screen
.
left
,
screen
.
top
,
screen
.
width
,
bounds
.
top
-
screen
.
top
,
));
}
if
(
screen
.
bottom
>
bounds
.
bottom
)
{
// There is a smaller sub-screen, below the display feature
newSubScreens
.
add
(
Rect
.
fromLTWH
(
screen
.
left
,
bounds
.
bottom
,
screen
.
width
,
screen
.
bottom
-
bounds
.
bottom
,
));
}
}
else
{
newSubScreens
.
add
(
screen
);
}
}
subScreens
=
newSubScreens
;
}
return
subScreens
;
}
static
Offset
_capOffset
(
Offset
offset
,
Size
maximum
)
{
if
(
offset
.
dx
>=
0
&&
offset
.
dx
<=
maximum
.
width
&&
offset
.
dy
>=
0
&&
offset
.
dy
<=
maximum
.
height
)
{
return
offset
;
}
else
{
return
Offset
(
math
.
min
(
math
.
max
(
0
,
offset
.
dx
),
maximum
.
width
),
math
.
min
(
math
.
max
(
0
,
offset
.
dy
),
maximum
.
height
),
);
}
}
}
packages/flutter/lib/src/widgets/media_query.dart
View file @
4a7e30f8
...
...
@@ -567,6 +567,55 @@ class MediaQueryData {
);
}
/// Creates a copy of this media query data by removing [displayFeatures] that
/// are completely outside the given sub-screen and adjusting the [padding],
/// [viewInsets] and [viewPadding] to be zero on the sides that are not
/// included in the sub-screen.
///
/// Returns unmodified [MediaQueryData] if the sub-screen coincides with the
/// available screen space.
///
/// Asserts in debug mode, if the given sub-screen is outside the available
/// screen space.
///
/// See also:
///
/// * [DisplayFeatureSubScreen], which removes the display features that
/// split the screen, from the [MediaQuery] and adds a [Padding] widget to
/// position the child to match the selected sub-screen.
MediaQueryData
removeDisplayFeatures
(
Rect
subScreen
)
{
assert
(
subScreen
.
left
>=
0.0
&&
subScreen
.
top
>=
0.0
&&
subScreen
.
right
<=
size
.
width
&&
subScreen
.
bottom
<=
size
.
height
,
"'subScreen' argument cannot be outside the bounds of the screen"
);
if
(
subScreen
.
size
==
size
&&
subScreen
.
topLeft
==
Offset
.
zero
)
return
this
;
final
double
rightInset
=
size
.
width
-
subScreen
.
right
;
final
double
bottomInset
=
size
.
height
-
subScreen
.
bottom
;
return
copyWith
(
padding:
EdgeInsets
.
only
(
left:
math
.
max
(
0.0
,
padding
.
left
-
subScreen
.
left
),
top:
math
.
max
(
0.0
,
padding
.
top
-
subScreen
.
top
),
right:
math
.
max
(
0.0
,
padding
.
right
-
rightInset
),
bottom:
math
.
max
(
0.0
,
padding
.
bottom
-
bottomInset
),
),
viewPadding:
EdgeInsets
.
only
(
left:
math
.
max
(
0.0
,
viewPadding
.
left
-
subScreen
.
left
),
top:
math
.
max
(
0.0
,
viewPadding
.
top
-
subScreen
.
top
),
right:
math
.
max
(
0.0
,
viewPadding
.
right
-
rightInset
),
bottom:
math
.
max
(
0.0
,
viewPadding
.
bottom
-
bottomInset
),
),
viewInsets:
EdgeInsets
.
only
(
left:
math
.
max
(
0.0
,
viewInsets
.
left
-
subScreen
.
left
),
top:
math
.
max
(
0.0
,
viewInsets
.
top
-
subScreen
.
top
),
right:
math
.
max
(
0.0
,
viewInsets
.
right
-
rightInset
),
bottom:
math
.
max
(
0.0
,
viewInsets
.
bottom
-
bottomInset
),
),
displayFeatures:
displayFeatures
.
where
(
(
ui
.
DisplayFeature
displayFeature
)
=>
subScreen
.
overlaps
(
displayFeature
.
bounds
)
).
toList
(),
);
}
@override
bool
operator
==(
Object
other
)
{
if
(
other
.
runtimeType
!=
runtimeType
)
...
...
packages/flutter/lib/widgets.dart
View file @
4a7e30f8
...
...
@@ -36,6 +36,7 @@ export 'src/widgets/debug.dart';
export
'src/widgets/default_text_editing_shortcuts.dart'
;
export
'src/widgets/desktop_text_selection_toolbar_layout_delegate.dart'
;
export
'src/widgets/dismissible.dart'
;
export
'src/widgets/display_feature_sub_screen.dart'
;
export
'src/widgets/disposable_build_context.dart'
;
export
'src/widgets/drag_target.dart'
;
export
'src/widgets/draggable_scrollable_sheet.dart'
;
...
...
packages/flutter/test/widgets/display_feature_sub_screen_test.dart
0 → 100644
View file @
4a7e30f8
// 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:ui'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
group
(
'DisplayFeatureSubScreen'
,
()
{
testWidgets
(
'without Directionality or anchor'
,
(
WidgetTester
tester
)
async
{
const
Key
childKey
=
Key
(
'childKey'
);
final
MediaQueryData
mediaQuery
=
MediaQueryData
.
fromWindow
(
WidgetsBinding
.
instance
!.
window
).
copyWith
(
displayFeatures:
<
DisplayFeature
>[
const
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
390
,
0
,
410
,
600
),
type:
DisplayFeatureType
.
hinge
,
state:
DisplayFeatureState
.
unknown
,
),
]
);
await
tester
.
pumpWidget
(
MediaQuery
(
data:
mediaQuery
,
child:
const
DisplayFeatureSubScreen
(
child:
SizedBox
(
key:
childKey
,
width:
double
.
infinity
,
height:
double
.
infinity
,
),
),
),
);
// With no Directionality or anchorpoint, the widget throws
final
String
message
=
tester
.
takeException
().
toString
();
expect
(
message
,
contains
(
'Directionality'
));
});
testWidgets
(
'with anchorPoint'
,
(
WidgetTester
tester
)
async
{
const
Key
childKey
=
Key
(
'childKey'
);
final
MediaQueryData
mediaQuery
=
MediaQueryData
.
fromWindow
(
WidgetsBinding
.
instance
!.
window
).
copyWith
(
displayFeatures:
<
DisplayFeature
>[
const
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
390
,
0
,
410
,
600
),
type:
DisplayFeatureType
.
hinge
,
state:
DisplayFeatureState
.
unknown
,
),
]
);
await
tester
.
pumpWidget
(
MediaQuery
(
data:
mediaQuery
,
child:
const
DisplayFeatureSubScreen
(
anchorPoint:
Offset
(
600
,
300
),
child:
SizedBox
(
key:
childKey
,
width:
double
.
infinity
,
height:
double
.
infinity
,
),
),
),
);
// anchorPoint is in the middle of the right screen
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byKey
(
childKey
));
expect
(
renderBox
.
size
.
width
,
equals
(
390.0
));
expect
(
renderBox
.
size
.
height
,
equals
(
600.0
));
expect
(
renderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
const
Offset
(
410
,
0
)));
});
testWidgets
(
'with infinity anchorpoint'
,
(
WidgetTester
tester
)
async
{
const
Key
childKey
=
Key
(
'childKey'
);
final
MediaQueryData
mediaQuery
=
MediaQueryData
.
fromWindow
(
WidgetsBinding
.
instance
!.
window
).
copyWith
(
displayFeatures:
<
DisplayFeature
>[
const
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
390
,
0
,
410
,
600
),
type:
DisplayFeatureType
.
hinge
,
state:
DisplayFeatureState
.
unknown
,
),
]
);
await
tester
.
pumpWidget
(
MediaQuery
(
data:
mediaQuery
,
child:
const
DisplayFeatureSubScreen
(
anchorPoint:
Offset
.
infinite
,
child:
SizedBox
(
key:
childKey
,
width:
double
.
infinity
,
height:
double
.
infinity
,
),
),
),
);
// anchorPoint is infinite, so the bottom-most & right-most screen is chosen
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byKey
(
childKey
));
expect
(
renderBox
.
size
.
width
,
equals
(
390.0
));
expect
(
renderBox
.
size
.
height
,
equals
(
600.0
));
expect
(
renderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
const
Offset
(
410
,
0
)));
});
testWidgets
(
'with horizontal hinge and anchorPoint'
,
(
WidgetTester
tester
)
async
{
const
Key
childKey
=
Key
(
'childKey'
);
final
MediaQueryData
mediaQuery
=
MediaQueryData
.
fromWindow
(
WidgetsBinding
.
instance
!.
window
).
copyWith
(
displayFeatures:
<
DisplayFeature
>[
const
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
0
,
290
,
800
,
310
),
type:
DisplayFeatureType
.
hinge
,
state:
DisplayFeatureState
.
unknown
,
),
]
);
await
tester
.
pumpWidget
(
MediaQuery
(
data:
mediaQuery
,
child:
const
DisplayFeatureSubScreen
(
anchorPoint:
Offset
(
1000
,
1000
),
child:
SizedBox
(
key:
childKey
,
width:
double
.
infinity
,
height:
double
.
infinity
,
),
),
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byKey
(
childKey
));
expect
(
renderBox
.
size
.
width
,
equals
(
800.0
));
expect
(
renderBox
.
size
.
height
,
equals
(
290.0
));
expect
(
renderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
const
Offset
(
0
,
310
)));
});
testWidgets
(
'with multiple display features and anchorPoint'
,
(
WidgetTester
tester
)
async
{
const
Key
childKey
=
Key
(
'childKey'
);
final
MediaQueryData
mediaQuery
=
MediaQueryData
.
fromWindow
(
WidgetsBinding
.
instance
!.
window
).
copyWith
(
displayFeatures:
<
DisplayFeature
>[
const
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
0
,
290
,
800
,
310
),
type:
DisplayFeatureType
.
hinge
,
state:
DisplayFeatureState
.
unknown
,
),
const
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
390
,
0
,
410
,
600
),
type:
DisplayFeatureType
.
hinge
,
state:
DisplayFeatureState
.
unknown
,
),
]
);
await
tester
.
pumpWidget
(
MediaQuery
(
data:
mediaQuery
,
child:
const
DisplayFeatureSubScreen
(
anchorPoint:
Offset
(
1000
,
1000
),
child:
SizedBox
(
key:
childKey
,
width:
double
.
infinity
,
height:
double
.
infinity
,
),
),
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byKey
(
childKey
));
expect
(
renderBox
.
size
.
width
,
equals
(
390.0
));
expect
(
renderBox
.
size
.
height
,
equals
(
290.0
));
expect
(
renderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
const
Offset
(
410
,
310
)));
});
testWidgets
(
'with non-splitting display features and anchorPoint'
,
(
WidgetTester
tester
)
async
{
const
Key
childKey
=
Key
(
'childKey'
);
final
MediaQueryData
mediaQuery
=
MediaQueryData
.
fromWindow
(
WidgetsBinding
.
instance
!.
window
).
copyWith
(
displayFeatures:
<
DisplayFeature
>[
// Top notch
const
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
100
,
0
,
700
,
100
),
type:
DisplayFeatureType
.
cutout
,
state:
DisplayFeatureState
.
unknown
,
),
// Bottom notch
const
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
100
,
500
,
700
,
600
),
type:
DisplayFeatureType
.
cutout
,
state:
DisplayFeatureState
.
unknown
,
),
]
);
await
tester
.
pumpWidget
(
MediaQuery
(
data:
mediaQuery
,
child:
const
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
DisplayFeatureSubScreen
(
child:
SizedBox
(
key:
childKey
,
width:
double
.
infinity
,
height:
double
.
infinity
,
),
),
),
),
);
// The display features provided are not wide enough to produce sub-screens
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byKey
(
childKey
));
expect
(
renderBox
.
size
.
width
,
equals
(
800.0
));
expect
(
renderBox
.
size
.
height
,
equals
(
600.0
));
expect
(
renderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
.
zero
));
});
});
}
packages/flutter/test/widgets/media_query_test.dart
View file @
4a7e30f8
...
...
@@ -760,4 +760,158 @@ void main() {
expect
(
settingsA
,
equals
(
settingsC
));
expect
(
settingsA
,
isNot
(
settingsB
));
});
testWidgets
(
'MediaQuery.removeDisplayFeatures removes specified display features and padding'
,
(
WidgetTester
tester
)
async
{
const
Size
size
=
Size
(
82.0
,
40.0
);
const
double
devicePixelRatio
=
2.0
;
const
double
textScaleFactor
=
1.2
;
const
EdgeInsets
padding
=
EdgeInsets
.
only
(
top:
1.0
,
right:
2.0
,
left:
3.0
,
bottom:
4.0
);
const
EdgeInsets
viewPadding
=
EdgeInsets
.
only
(
top:
6.0
,
right:
8.0
,
left:
10.0
,
bottom:
12.0
);
const
EdgeInsets
viewInsets
=
EdgeInsets
.
only
(
top:
5.0
,
right:
6.0
,
left:
7.0
,
bottom:
8.0
);
const
List
<
DisplayFeature
>
displayFeatures
=
<
DisplayFeature
>[
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
40
,
0
,
42
,
40
),
type:
DisplayFeatureType
.
hinge
,
state:
DisplayFeatureState
.
postureFlat
,
),
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
70
,
10
,
74
,
14
),
type:
DisplayFeatureType
.
cutout
,
state:
DisplayFeatureState
.
unknown
,
),
];
// A section of the screen that intersects no display feature or padding area
const
Rect
subScreen
=
Rect
.
fromLTRB
(
20
,
10
,
40
,
20
);
late
MediaQueryData
subScreenMediaQuery
;
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
size:
size
,
devicePixelRatio:
devicePixelRatio
,
textScaleFactor:
textScaleFactor
,
padding:
padding
,
viewPadding:
viewPadding
,
viewInsets:
viewInsets
,
alwaysUse24HourFormat:
true
,
accessibleNavigation:
true
,
invertColors:
true
,
disableAnimations:
true
,
boldText:
true
,
highContrast:
true
,
displayFeatures:
displayFeatures
,
),
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
MediaQuery
(
data:
MediaQuery
.
of
(
context
).
removeDisplayFeatures
(
subScreen
),
child:
Builder
(
builder:
(
BuildContext
context
)
{
subScreenMediaQuery
=
MediaQuery
.
of
(
context
);
return
Container
();
},
),
);
},
),
),
);
expect
(
subScreenMediaQuery
.
size
,
size
);
expect
(
subScreenMediaQuery
.
devicePixelRatio
,
devicePixelRatio
);
expect
(
subScreenMediaQuery
.
textScaleFactor
,
textScaleFactor
);
expect
(
subScreenMediaQuery
.
padding
,
EdgeInsets
.
zero
);
expect
(
subScreenMediaQuery
.
viewPadding
,
EdgeInsets
.
zero
);
expect
(
subScreenMediaQuery
.
viewInsets
,
EdgeInsets
.
zero
);
expect
(
subScreenMediaQuery
.
alwaysUse24HourFormat
,
true
);
expect
(
subScreenMediaQuery
.
accessibleNavigation
,
true
);
expect
(
subScreenMediaQuery
.
invertColors
,
true
);
expect
(
subScreenMediaQuery
.
disableAnimations
,
true
);
expect
(
subScreenMediaQuery
.
boldText
,
true
);
expect
(
subScreenMediaQuery
.
highContrast
,
true
);
expect
(
subScreenMediaQuery
.
displayFeatures
,
isEmpty
);
});
testWidgets
(
'MediaQuery.removePadding only removes specified display features and padding'
,
(
WidgetTester
tester
)
async
{
const
Size
size
=
Size
(
82.0
,
40.0
);
const
double
devicePixelRatio
=
2.0
;
const
double
textScaleFactor
=
1.2
;
const
EdgeInsets
padding
=
EdgeInsets
.
only
(
top:
1.0
,
right:
2.0
,
left:
3.0
,
bottom:
4.0
);
const
EdgeInsets
viewPadding
=
EdgeInsets
.
only
(
top:
6.0
,
right:
8.0
,
left:
46.0
,
bottom:
12.0
);
const
EdgeInsets
viewInsets
=
EdgeInsets
.
only
(
top:
5.0
,
right:
6.0
,
left:
7.0
,
bottom:
8.0
);
const
DisplayFeature
cutoutDisplayFeature
=
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
70
,
10
,
74
,
14
),
type:
DisplayFeatureType
.
cutout
,
state:
DisplayFeatureState
.
unknown
,
);
const
List
<
DisplayFeature
>
displayFeatures
=
<
DisplayFeature
>[
DisplayFeature
(
bounds:
Rect
.
fromLTRB
(
40
,
0
,
42
,
40
),
type:
DisplayFeatureType
.
hinge
,
state:
DisplayFeatureState
.
postureFlat
,
),
cutoutDisplayFeature
,
];
// A section of the screen that does contain display features and padding
const
Rect
subScreen
=
Rect
.
fromLTRB
(
42
,
0
,
82
,
40
);
late
MediaQueryData
subScreenMediaQuery
;
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
size:
size
,
devicePixelRatio:
devicePixelRatio
,
textScaleFactor:
textScaleFactor
,
padding:
padding
,
viewPadding:
viewPadding
,
viewInsets:
viewInsets
,
alwaysUse24HourFormat:
true
,
accessibleNavigation:
true
,
invertColors:
true
,
disableAnimations:
true
,
boldText:
true
,
highContrast:
true
,
displayFeatures:
displayFeatures
,
),
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
MediaQuery
(
data:
MediaQuery
.
of
(
context
).
removeDisplayFeatures
(
subScreen
),
child:
Builder
(
builder:
(
BuildContext
context
)
{
subScreenMediaQuery
=
MediaQuery
.
of
(
context
);
return
Container
();
},
),
);
},
),
),
);
expect
(
subScreenMediaQuery
.
size
,
size
);
expect
(
subScreenMediaQuery
.
devicePixelRatio
,
devicePixelRatio
);
expect
(
subScreenMediaQuery
.
textScaleFactor
,
textScaleFactor
);
expect
(
subScreenMediaQuery
.
padding
,
const
EdgeInsets
.
only
(
top:
1.0
,
right:
2.0
,
bottom:
4.0
),
);
expect
(
subScreenMediaQuery
.
viewPadding
,
const
EdgeInsets
.
only
(
top:
6.0
,
left:
4.0
,
right:
8.0
,
bottom:
12.0
),
);
expect
(
subScreenMediaQuery
.
viewInsets
,
const
EdgeInsets
.
only
(
top:
5.0
,
right:
6.0
,
bottom:
8.0
),
);
expect
(
subScreenMediaQuery
.
alwaysUse24HourFormat
,
true
);
expect
(
subScreenMediaQuery
.
accessibleNavigation
,
true
);
expect
(
subScreenMediaQuery
.
invertColors
,
true
);
expect
(
subScreenMediaQuery
.
disableAnimations
,
true
);
expect
(
subScreenMediaQuery
.
boldText
,
true
);
expect
(
subScreenMediaQuery
.
highContrast
,
true
);
expect
(
subScreenMediaQuery
.
displayFeatures
,
<
DisplayFeature
>[
cutoutDisplayFeature
]);
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment