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
3a181e49
Unverified
Commit
3a181e49
authored
Jan 13, 2023
by
Hans Muller
Committed by
GitHub
Jan 13, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added LinearBorder, an OutlinedBorder like BoxBorder (#116940)
parent
fb1a1510
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
901 additions
and
0 deletions
+901
-0
linear_border.0.dart
examples/api/lib/painting/linear_border/linear_border.0.dart
+303
-0
linear_border.0_test.dart
examples/api/test/painting/linear_border.0_test.dart
+40
-0
painting.dart
packages/flutter/lib/painting.dart
+1
-0
linear_border.dart
packages/flutter/lib/src/painting/linear_border.dart
+389
-0
linear_border_test.dart
packages/flutter/test/painting/linear_border_test.dart
+168
-0
No files found.
examples/api/lib/painting/linear_border/linear_border.0.dart
0 → 100644
View file @
3a181e49
// 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.
// Examples of LinearBorder and LinearBorderEdge.
import
'package:flutter/material.dart'
;
void
main
(
)
{
runApp
(
const
ExampleApp
());
}
class
ExampleApp
extends
StatelessWidget
{
const
ExampleApp
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
MaterialApp
(
theme:
ThemeData
.
light
(
useMaterial3:
true
),
home:
const
Directionality
(
textDirection:
TextDirection
.
ltr
,
// Or try rtl.
child:
Home
(),
),
);
}
}
class
SampleCard
extends
StatelessWidget
{
const
SampleCard
({
super
.
key
,
required
this
.
title
,
required
this
.
subtitle
,
required
this
.
children
});
final
String
title
;
final
String
subtitle
;
final
List
<
Widget
>
children
;
@override
Widget
build
(
BuildContext
context
)
{
final
ThemeData
theme
=
Theme
.
of
(
context
);
final
TextTheme
textTheme
=
theme
.
textTheme
;
final
ColorScheme
colorScheme
=
theme
.
colorScheme
;
return
Card
(
child:
Padding
(
padding:
const
EdgeInsets
.
all
(
16
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
Text
(
title
,
style:
textTheme
.
titleMedium
),
Text
(
subtitle
,
style:
textTheme
.
bodyMedium
!.
copyWith
(
color:
colorScheme
.
secondary
)),
const
SizedBox
(
height:
16
),
Row
(
children:
List
<
Widget
>.
generate
(
children
.
length
*
2
-
1
,
(
int
index
)
{
return
index
.
isEven
?
children
[
index
~/
2
]
:
const
SizedBox
(
width:
16
);
}),
),
],
),
),
);
}
}
class
Home
extends
StatefulWidget
{
const
Home
({
super
.
key
});
@override
State
<
Home
>
createState
()
=>
_HomeState
();
}
class
_HomeState
extends
State
<
Home
>
{
final
LinearBorder
shape0
=
LinearBorder
.
top
();
final
LinearBorder
shape1
=
LinearBorder
.
top
(
size:
0
);
late
LinearBorder
shape
=
shape0
;
@override
Widget
build
(
BuildContext
context
)
{
final
ColorScheme
colorScheme
=
Theme
.
of
(
context
).
colorScheme
;
final
BorderSide
primarySide0
=
BorderSide
(
width:
0
,
color:
colorScheme
.
inversePrimary
);
// hairline
final
BorderSide
primarySide2
=
BorderSide
(
width:
2
,
color:
colorScheme
.
onPrimaryContainer
);
final
BorderSide
primarySide3
=
BorderSide
(
width:
3
,
color:
colorScheme
.
inversePrimary
);
return
Scaffold
(
body:
SingleChildScrollView
(
child:
Padding
(
padding:
const
EdgeInsets
.
all
(
16
),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
// Demonstrates using LinearBorder.bottom() to define
// an underline border for the standard button types.
// The underline's color and width is defined by the ButtonStyle's
// side parameter. The side can also be specified as a
// LinearBorder parameter and if both are specified then the
// ButtonStyle's side is used. This set up makes it possible
// for a button theme to specify the shape and for indidividual
// buttons to specify the shape border's color and width.
SampleCard
(
title:
'LinearBorder.bottom()'
,
subtitle:
'Standard button widgets'
,
children:
<
Widget
>[
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide3
,
shape:
LinearBorder
.
bottom
(),
),
onPressed:
()
{
},
child:
const
Text
(
'Text'
),
),
OutlinedButton
(
style:
OutlinedButton
.
styleFrom
(
side:
primarySide3
,
shape:
LinearBorder
.
bottom
(),
),
onPressed:
()
{
},
child:
const
Text
(
'Outlined'
),
),
ElevatedButton
(
style:
ElevatedButton
.
styleFrom
(
side:
primarySide3
,
shape:
LinearBorder
.
bottom
(),
),
onPressed:
()
{
},
child:
const
Text
(
'Elevated'
),
),
],
),
const
SizedBox
(
height:
32
),
// Demonstrates creating LinearBorders with a single edge
// by using the convenience constructors like LinearBorder.start().
// The edges are drawn with a BorderSide with width:0, which
// means that a "hairline" line is stroked. Wider borders are
// drawn with filled rectangles.
SampleCard
(
title:
'LinearBorder'
,
subtitle:
'Convenience constructors'
,
children:
<
Widget
>[
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide0
,
shape:
LinearBorder
.
start
(),
),
onPressed:
()
{
},
child:
const
Text
(
'Start()'
),
),
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide0
,
shape:
LinearBorder
.
end
(),
),
onPressed:
()
{
},
child:
const
Text
(
'End()'
),
),
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide0
,
shape:
LinearBorder
.
top
(),
),
onPressed:
()
{
},
child:
const
Text
(
'Top()'
),
),
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide0
,
shape:
LinearBorder
.
bottom
(),
),
onPressed:
()
{
},
child:
const
Text
(
'Bottom()'
),
),
],
),
const
SizedBox
(
height:
32
),
// Demonstrates creating LinearBorders with a single edge
// that's smaller than the button's bounding box. The size
// parameter specifies a percentage of the available space
// and alignment is -1 for start-alignment, 0 for centered,
// and 1 for end-alignment.
SampleCard
(
title:
'LinearBorder'
,
subtitle:
'Size and alignment parameters'
,
children:
<
Widget
>[
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide2
,
shape:
LinearBorder
.
bottom
(
size:
0.5
,
),
),
onPressed:
()
{
},
child:
const
Text
(
'Center'
),
),
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide2
,
shape:
LinearBorder
.
bottom
(
size:
0.75
,
alignment:
-
1
,
),
),
onPressed:
()
{
},
child:
const
Text
(
'Start'
),
),
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide2
,
shape:
LinearBorder
.
bottom
(
size:
0.75
,
alignment:
1
,
),
),
onPressed:
()
{
},
child:
const
Text
(
'End'
),
),
],
),
const
SizedBox
(
height:
32
),
// Demonstrates creating LinearBorders with more than one edge.
// In these cases the default constructor is used and each edge
// is defined with one LinearBorderEdge object.
SampleCard
(
title:
'LinearBorder'
,
subtitle:
'LinearBorderEdge parameters'
,
children:
<
Widget
>[
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide0
,
shape:
const
LinearBorder
(
top:
LinearBorderEdge
(),
bottom:
LinearBorderEdge
(),
),
),
onPressed:
()
{
},
child:
const
Text
(
'Horizontal'
),
),
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide0
,
shape:
const
LinearBorder
(
start:
LinearBorderEdge
(),
end:
LinearBorderEdge
(),
),
),
onPressed:
()
{
},
child:
const
Text
(
'Vertical'
),
),
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide0
,
shape:
const
LinearBorder
(
start:
LinearBorderEdge
(),
bottom:
LinearBorderEdge
(),
),
),
onPressed:
()
{
},
child:
const
Text
(
'Corner'
),
),
],
),
const
SizedBox
(
height:
32
),
// Demonstrates that changing properties of LinearBorders
// causes them to animate to their new configuration.
SampleCard
(
title:
'Interpolation'
,
subtitle:
'LinearBorder.top() => LinearBorder.top(size: 0)'
,
children:
<
Widget
>[
IconButton
(
icon:
const
Icon
(
Icons
.
play_arrow
),
onPressed:
()
{
setState
(()
{
shape
=
shape
==
shape0
?
shape1
:
shape0
;
});
},
),
TextButton
(
style:
TextButton
.
styleFrom
(
side:
primarySide3
,
shape:
shape
,
),
onPressed:
()
{
},
child:
const
Text
(
'Press Play'
),
),
TextButton
(
style:
ButtonStyle
(
side:
MaterialStateProperty
.
resolveWith
<
BorderSide
?>((
Set
<
MaterialState
>
states
)
{
return
states
.
contains
(
MaterialState
.
hovered
)
?
primarySide3
:
null
;
}),
shape:
MaterialStateProperty
.
resolveWith
<
OutlinedBorder
>((
Set
<
MaterialState
>
states
)
{
return
states
.
contains
(
MaterialState
.
hovered
)
?
shape0
:
shape1
;
}),
),
onPressed:
()
{
},
child:
const
Text
(
'Hover'
),
),
],
),
],
),
),
),
);
}
}
examples/api/test/painting/linear_border.0_test.dart
0 → 100644
View file @
3a181e49
// 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/material.dart'
;
import
'package:flutter_api_samples/painting/linear_border/linear_border.0.dart'
as
example
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'Smoke Test'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
ExampleApp
(),
);
expect
(
find
.
byType
(
example
.
Home
),
findsOneWidget
);
// Scroll the interpolation example into view
await
tester
.
scrollUntilVisible
(
find
.
byIcon
(
Icons
.
play_arrow
),
500.0
,
scrollable:
find
.
byType
(
Scrollable
),
);
expect
(
find
.
byIcon
(
Icons
.
play_arrow
),
findsOneWidget
);
// Run the interpolation example
await
tester
.
tap
(
find
.
byIcon
(
Icons
.
play_arrow
));
await
tester
.
pumpAndSettle
();
await
tester
.
tap
(
find
.
byIcon
(
Icons
.
play_arrow
));
await
tester
.
pumpAndSettle
();
final
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
text
(
'Interpolation'
)));
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
text
(
'Hover'
)));
await
tester
.
pumpAndSettle
();
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
text
(
'Interpolation'
)));
await
tester
.
pumpAndSettle
();
});
}
packages/flutter/lib/painting.dart
View file @
3a181e49
...
...
@@ -47,6 +47,7 @@ export 'src/painting/image_provider.dart';
export
'src/painting/image_resolution.dart'
;
export
'src/painting/image_stream.dart'
;
export
'src/painting/inline_span.dart'
;
export
'src/painting/linear_border.dart'
;
export
'src/painting/matrix_utils.dart'
;
export
'src/painting/notched_shapes.dart'
;
export
'src/painting/oval_border.dart'
;
...
...
packages/flutter/lib/src/painting/linear_border.dart
0 → 100644
View file @
3a181e49
// 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'
show
lerpDouble
;
import
'package:flutter/foundation.dart'
;
import
'basic_types.dart'
;
import
'borders.dart'
;
import
'edge_insets.dart'
;
/// Defines the relative size and alignment of one <LinearBorder> edge.
///
/// A [LinearBorder] defines a box outline as zero to four edges, each
/// of which is rendered as a single line. The width and color of the
/// lines is defined by [LinearBorder.side].
///
/// Each line's length is defined by [size], a value between 0.0 and 1.0
/// (the default) which defines the length as a percentage of the
/// length of a box edge.
///
/// When [size] is less than 1.0, the line is aligned within the
/// available space according to [alignment], a value between -1.0 and
/// 1.0. The default is 0.0, which means centered, -1.0 means align on the
/// "start" side, and 1.0 means align on the "end" side. The meaning of
/// start and end depend on the current [TextDirection], see
/// [Directionality].
@immutable
class
LinearBorderEdge
{
/// Defines one side of a [LinearBorder].
///
/// The values of [size] and [alignment] must be between
/// 0.0 and 1.0, and -1.0 and 1.0 respectively.
const
LinearBorderEdge
({
this
.
size
=
1.0
,
this
.
alignment
=
0.0
,
})
:
assert
(
size
>=
0.0
&&
size
<=
1.0
);
/// A value between 0.0 and 1.0 that defines the length of the edge as a
/// percentage of the length of the corresponding box
/// edge. Default is 1.0.
final
double
size
;
/// A value between -1.0 and 1.0 that defines how edges for which [size]
/// is less than 1.0 are aligned relative to the corresponding box edge.
///
/// * -1.0, aligned in the "start" direction. That's left
/// for [TextDirection.ltr] and right for [TextDirection.rtl].
/// * 0.0, centered.
/// * 1.0, aligned in the "end" direction. That's right
/// for [TextDirection.ltr] and left for [TextDirection.rtl].
final
double
alignment
;
/// Linearly interpolates between two [LinearBorder]s.
///
/// If both `a` and `b` are null then null is returned. If `a` is null
/// then we interpolate to `b` varying [size] from 0.0 to `b.size`. If `b`
/// is null then we interpolate from `a` varying size from `a.size` to zero.
/// Otherwise both values are interpolated.
static
LinearBorderEdge
?
lerp
(
LinearBorderEdge
?
a
,
LinearBorderEdge
?
b
,
double
t
)
{
if
(
a
==
null
&&
b
==
null
)
{
return
null
;
}
a
??=
LinearBorderEdge
(
alignment:
b
!.
alignment
,
size:
0
);
b
??=
LinearBorderEdge
(
alignment:
a
.
alignment
,
size:
0
);
return
LinearBorderEdge
(
size:
lerpDouble
(
a
.
size
,
b
.
size
,
t
)!,
alignment:
lerpDouble
(
a
.
alignment
,
b
.
alignment
,
t
)!,
);
}
@override
bool
operator
==(
Object
other
)
{
if
(
identical
(
this
,
other
))
{
return
true
;
}
if
(
other
.
runtimeType
!=
runtimeType
)
{
return
false
;
}
return
other
is
LinearBorderEdge
&&
other
.
size
==
size
&&
other
.
alignment
==
alignment
;
}
@override
int
get
hashCode
=>
Object
.
hash
(
size
,
alignment
);
@override
String
toString
()
{
final
StringBuffer
s
=
StringBuffer
(
'
${objectRuntimeType(this, 'LinearBorderEdge')}
('
);
if
(
size
!=
1.0
)
{
s
.
write
(
'size:
$size
'
);
}
if
(
alignment
!=
0
)
{
final
String
comma
=
size
!=
1.0
?
', '
:
''
;
s
.
write
(
'
${comma}
alignment:
$alignment
'
);
}
s
.
write
(
')'
);
return
s
.
toString
();
}
}
/// An [OutlinedBorder] like [BoxBorder] that allows one to define a rectangular (box) border
/// in terms of zero to four [LinearBorderEdge]s, each of which is rendered as a single line.
///
/// The color and width of each line are defined by [side]. When [LinearBorder] is used
/// with a class whose border sides and shape are defined by a [ButtonStyle], then a non-null
/// [ButtonStyle.side] will override the one specified here. For example the [LinearBorder]
/// in the [TextButton] example below adds a red underline to the button. This is because
/// TextButton's `side` parameter overrides the `side` property of its [ButtonStyle.shape].
///
/// ```dart
/// TextButton(
/// style: TextButton.styleFrom(
/// side: const BorderSide(color: Colors.red),
/// shape: const LinearBorder(
/// side: BorderSide(color: Colors.blue),
/// bottom: LinearBorderEdge(),
/// ),
/// ),
/// onPressed: () { },
/// child: const Text('Red LinearBorder'),
/// )
///```
///
/// This class resolves itself against the current [TextDirection] (see [Directionality]).
/// Start and end values resolve to left and right for [TextDirection.ltr] and to
/// right and left for [TextDirection.rtl].
///
/// Convenience constructors are included for the common case where just one edge is specified:
/// [LinearBorder.start], [LinearBorder.end], [LinearBorder.top], [LinearBorder.bottom].
class
LinearBorder
extends
OutlinedBorder
{
/// Creates a rectangular box border that's rendered as zero to four lines.
const
LinearBorder
({
super
.
side
,
this
.
start
,
this
.
end
,
this
.
top
,
this
.
bottom
,
});
/// Creates a rectangular box border with an edge on the left for [TextDirection.ltr]
/// or on the right for [TextDirection.rtl].
LinearBorder
.
start
({
super
.
side
,
double
alignment
=
0.0
,
double
size
=
1.0
})
:
start
=
LinearBorderEdge
(
alignment:
alignment
,
size:
size
),
end
=
null
,
top
=
null
,
bottom
=
null
;
/// Creates a rectangular box border with an edge on the right for [TextDirection.ltr]
/// or on the left for [TextDirection.rtl].
LinearBorder
.
end
({
super
.
side
,
double
alignment
=
0.0
,
double
size
=
1.0
})
:
start
=
null
,
end
=
LinearBorderEdge
(
alignment:
alignment
,
size:
size
),
top
=
null
,
bottom
=
null
;
/// Creates a rectangular box border with an edge on the top.
LinearBorder
.
top
({
super
.
side
,
double
alignment
=
0.0
,
double
size
=
1.0
})
:
start
=
null
,
end
=
null
,
top
=
LinearBorderEdge
(
alignment:
alignment
,
size:
size
),
bottom
=
null
;
/// Creates a rectangular box border with an edge on the bottom.
LinearBorder
.
bottom
({
super
.
side
,
double
alignment
=
0.0
,
double
size
=
1.0
})
:
start
=
null
,
end
=
null
,
top
=
null
,
bottom
=
LinearBorderEdge
(
alignment:
alignment
,
size:
size
);
/// No border.
static
const
LinearBorder
none
=
LinearBorder
();
/// Defines the left edge for [TextDirection.ltr] or the right
/// for [TextDirection.rtl].
final
LinearBorderEdge
?
start
;
/// Defines the right edge for [TextDirection.ltr] or the left
/// for [TextDirection.rtl].
final
LinearBorderEdge
?
end
;
/// Defines the top edge.
final
LinearBorderEdge
?
top
;
/// Defines the bottom edge.
final
LinearBorderEdge
?
bottom
;
@override
LinearBorder
scale
(
double
t
)
{
return
LinearBorder
(
side:
side
.
scale
(
t
),
);
}
@override
EdgeInsetsGeometry
get
dimensions
{
final
double
width
=
side
.
width
;
return
EdgeInsetsDirectional
.
fromSTEB
(
start
==
null
?
0.0
:
width
,
top
==
null
?
0.0
:
width
,
end
==
null
?
0.0
:
width
,
bottom
==
null
?
0.0
:
width
,
);
}
@override
ShapeBorder
?
lerpFrom
(
ShapeBorder
?
a
,
double
t
)
{
if
(
a
is
LinearBorder
)
{
return
LinearBorder
(
side:
BorderSide
.
lerp
(
a
.
side
,
side
,
t
),
start:
LinearBorderEdge
.
lerp
(
a
.
start
,
start
,
t
),
end:
LinearBorderEdge
.
lerp
(
a
.
end
,
end
,
t
),
top:
LinearBorderEdge
.
lerp
(
a
.
top
,
top
,
t
),
bottom:
LinearBorderEdge
.
lerp
(
a
.
bottom
,
bottom
,
t
),
);
}
return
super
.
lerpFrom
(
a
,
t
);
}
@override
ShapeBorder
?
lerpTo
(
ShapeBorder
?
b
,
double
t
)
{
if
(
b
is
LinearBorder
)
{
return
LinearBorder
(
side:
BorderSide
.
lerp
(
side
,
b
.
side
,
t
),
start:
LinearBorderEdge
.
lerp
(
start
,
b
.
start
,
t
),
end:
LinearBorderEdge
.
lerp
(
end
,
b
.
end
,
t
),
top:
LinearBorderEdge
.
lerp
(
top
,
b
.
top
,
t
),
bottom:
LinearBorderEdge
.
lerp
(
bottom
,
b
.
bottom
,
t
),
);
}
return
super
.
lerpTo
(
b
,
t
);
}
/// Returns a copy of this LinearBorder with the given fields replaced with
/// the new values.
@override
LinearBorder
copyWith
({
BorderSide
?
side
,
LinearBorderEdge
?
start
,
LinearBorderEdge
?
end
,
LinearBorderEdge
?
top
,
LinearBorderEdge
?
bottom
,
})
{
return
LinearBorder
(
side:
side
??
this
.
side
,
start:
start
??
this
.
start
,
end:
end
??
this
.
end
,
top:
top
??
this
.
top
,
bottom:
bottom
??
this
.
bottom
,
);
}
@override
Path
getInnerPath
(
Rect
rect
,
{
TextDirection
?
textDirection
})
{
final
Rect
adjustedRect
=
dimensions
.
resolve
(
textDirection
).
deflateRect
(
rect
);
return
Path
()
..
addRect
(
adjustedRect
);
}
@override
Path
getOuterPath
(
Rect
rect
,
{
TextDirection
?
textDirection
})
{
return
Path
()
..
addRect
(
rect
);
}
@override
void
paint
(
Canvas
canvas
,
Rect
rect
,
{
TextDirection
?
textDirection
})
{
final
EdgeInsets
insets
=
dimensions
.
resolve
(
textDirection
);
final
bool
rtl
=
textDirection
==
TextDirection
.
rtl
;
final
Path
path
=
Path
();
final
Paint
paint
=
Paint
()
..
strokeWidth
=
0.0
;
void
drawEdge
(
Rect
rect
,
Color
color
)
{
paint
.
color
=
color
;
path
.
reset
();
path
.
moveTo
(
rect
.
left
,
rect
.
top
);
if
(
rect
.
width
==
0.0
)
{
paint
.
style
=
PaintingStyle
.
stroke
;
path
.
lineTo
(
rect
.
left
,
rect
.
bottom
);
}
else
if
(
rect
.
height
==
0.0
)
{
paint
.
style
=
PaintingStyle
.
stroke
;
path
.
lineTo
(
rect
.
right
,
rect
.
top
);
}
else
{
paint
.
style
=
PaintingStyle
.
fill
;
path
.
lineTo
(
rect
.
right
,
rect
.
top
);
path
.
lineTo
(
rect
.
right
,
rect
.
bottom
);
path
.
lineTo
(
rect
.
left
,
rect
.
bottom
);
}
canvas
.
drawPath
(
path
,
paint
);
}
if
(
start
!=
null
&&
start
!.
size
!=
0.0
&&
side
.
style
!=
BorderStyle
.
none
)
{
final
Rect
insetRect
=
Rect
.
fromLTWH
(
rect
.
left
,
rect
.
top
+
insets
.
top
,
rect
.
width
,
rect
.
height
-
insets
.
vertical
);
final
double
x
=
rtl
?
rect
.
right
-
insets
.
right
:
rect
.
left
;
final
double
width
=
rtl
?
insets
.
right
:
insets
.
left
;
final
double
height
=
insetRect
.
height
*
start
!.
size
;
final
double
y
=
(
insetRect
.
height
-
height
)
*
((
start
!.
alignment
+
1.0
)
/
2.0
);
final
Rect
r
=
Rect
.
fromLTWH
(
x
,
y
,
width
,
height
);
drawEdge
(
r
,
side
.
color
);
}
if
(
end
!=
null
&&
end
!.
size
!=
0.0
&&
side
.
style
!=
BorderStyle
.
none
)
{
final
Rect
insetRect
=
Rect
.
fromLTWH
(
rect
.
left
,
rect
.
top
+
insets
.
top
,
rect
.
width
,
rect
.
height
-
insets
.
vertical
);
final
double
x
=
rtl
?
rect
.
left
:
rect
.
right
-
insets
.
right
;
final
double
width
=
rtl
?
insets
.
left
:
insets
.
right
;
final
double
height
=
insetRect
.
height
*
end
!.
size
;
final
double
y
=
(
insetRect
.
height
-
height
)
*
((
end
!.
alignment
+
1.0
)
/
2.0
);
final
Rect
r
=
Rect
.
fromLTWH
(
x
,
y
,
width
,
height
);
drawEdge
(
r
,
side
.
color
);
}
if
(
top
!=
null
&&
top
!.
size
!=
0.0
&&
side
.
style
!=
BorderStyle
.
none
)
{
final
double
width
=
rect
.
width
*
top
!.
size
;
final
double
startX
=
(
rect
.
width
-
width
)
*
((
top
!.
alignment
+
1.0
)
/
2.0
);
final
double
x
=
rtl
?
rect
.
width
-
startX
-
width
:
startX
;
final
Rect
r
=
Rect
.
fromLTWH
(
x
,
rect
.
top
,
width
,
insets
.
top
);
drawEdge
(
r
,
side
.
color
);
}
if
(
bottom
!=
null
&&
bottom
!.
size
!=
0.0
&&
side
.
style
!=
BorderStyle
.
none
)
{
final
double
width
=
rect
.
width
*
bottom
!.
size
;
final
double
startX
=
(
rect
.
width
-
width
)
*
((
bottom
!.
alignment
+
1.0
)
/
2.0
);
final
double
x
=
rtl
?
rect
.
width
-
startX
-
width:
startX
;
final
Rect
r
=
Rect
.
fromLTWH
(
x
,
rect
.
bottom
-
insets
.
bottom
,
width
,
side
.
width
);
drawEdge
(
r
,
side
.
color
);
}
}
@override
bool
operator
==(
Object
other
)
{
if
(
identical
(
this
,
other
))
{
return
true
;
}
if
(
other
.
runtimeType
!=
runtimeType
)
{
return
false
;
}
return
other
is
LinearBorder
&&
other
.
side
==
side
&&
other
.
start
==
start
&&
other
.
end
==
end
&&
other
.
top
==
top
&&
other
.
bottom
==
bottom
;
}
@override
int
get
hashCode
=>
Object
.
hash
(
side
,
start
,
end
,
top
,
bottom
);
@override
String
toString
()
{
if
(
this
==
LinearBorder
.
none
)
{
return
'LinearBorder.none'
;
}
final
StringBuffer
s
=
StringBuffer
(
'
${objectRuntimeType(this, 'LinearBorder')}
(side:
$side
'
);
if
(
start
!=
null
)
{
s
.
write
(
', start:
$start
'
);
}
if
(
end
!=
null
)
{
s
.
write
(
', end:
$end
'
);
}
if
(
top
!=
null
)
{
s
.
write
(
', top:
$top
'
);
}
if
(
bottom
!=
null
)
{
s
.
write
(
', bottom:
$bottom
'
);
}
s
.
write
(
')'
);
return
s
.
toString
();
}
}
packages/flutter/test/painting/linear_border_test.dart
0 → 100644
View file @
3a181e49
// 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/painting.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'../rendering/mock_canvas.dart'
;
const
Rect
canvasRect
=
Rect
.
fromLTWH
(
0
,
0
,
100
,
100
);
const
BorderSide
borderSide
=
BorderSide
(
width:
4
,
color:
Color
(
0x0f00ff00
));
// Test points for rectangular filled paths based on a BorderSide with width 4 and
// a 100x100 bounding rectangle (canvasRect).
List
<
Offset
>
rectIncludes
(
Rect
r
)
{
return
<
Offset
>[
r
.
topLeft
,
r
.
topRight
,
r
.
bottomLeft
,
r
.
bottomRight
,
r
.
center
];
}
final
List
<
Offset
>
leftRectIncludes
=
rectIncludes
(
const
Rect
.
fromLTWH
(
0
,
0
,
4
,
100
));
final
List
<
Offset
>
rightRectIncludes
=
rectIncludes
(
const
Rect
.
fromLTWH
(
96
,
0
,
4
,
100
));
final
List
<
Offset
>
topRectIncludes
=
rectIncludes
(
const
Rect
.
fromLTWH
(
0
,
0
,
100
,
4
));
final
List
<
Offset
>
bottomRectIncludes
=
rectIncludes
(
const
Rect
.
fromLTWH
(
0
,
96
,
100
,
4
));
void
main
(
)
{
test
(
'LinearBorderEdge defaults'
,
()
{
expect
(
const
LinearBorderEdge
().
size
,
1
);
expect
(
const
LinearBorderEdge
().
alignment
,
0
);
});
test
(
'LinearBorder defaults'
,
()
{
void
expectEmptyBorder
(
LinearBorder
border
)
{
expect
(
border
.
side
,
BorderSide
.
none
);
expect
(
border
.
dimensions
,
EdgeInsets
.
zero
);
expect
(
border
.
preferPaintInterior
,
false
);
expect
(
border
.
start
,
null
);
expect
(
border
.
end
,
null
);
expect
(
border
.
top
,
null
);
expect
(
border
.
bottom
,
null
);
}
expectEmptyBorder
(
LinearBorder
.
none
);
expect
(
LinearBorder
.
start
().
side
,
BorderSide
.
none
);
expect
(
LinearBorder
.
start
().
start
,
const
LinearBorderEdge
());
expect
(
LinearBorder
.
start
().
end
,
null
);
expect
(
LinearBorder
.
start
().
top
,
null
);
expect
(
LinearBorder
.
start
().
bottom
,
null
);
expect
(
LinearBorder
.
end
().
side
,
BorderSide
.
none
);
expect
(
LinearBorder
.
end
().
start
,
null
);
expect
(
LinearBorder
.
end
().
end
,
const
LinearBorderEdge
());
expect
(
LinearBorder
.
end
().
top
,
null
);
expect
(
LinearBorder
.
end
().
bottom
,
null
);
expect
(
LinearBorder
.
top
().
side
,
BorderSide
.
none
);
expect
(
LinearBorder
.
top
().
start
,
null
);
expect
(
LinearBorder
.
top
().
end
,
null
);
expect
(
LinearBorder
.
top
().
top
,
const
LinearBorderEdge
());
expect
(
LinearBorder
.
top
().
bottom
,
null
);
expect
(
LinearBorder
.
bottom
().
side
,
BorderSide
.
none
);
expect
(
LinearBorder
.
bottom
().
start
,
null
);
expect
(
LinearBorder
.
bottom
().
end
,
null
);
expect
(
LinearBorder
.
bottom
().
top
,
null
);
expect
(
LinearBorder
.
bottom
().
bottom
,
const
LinearBorderEdge
());
});
test
(
'LinearBorder copyWith, ==, hashCode'
,
()
{
expect
(
LinearBorder
.
none
,
LinearBorder
.
none
.
copyWith
());
expect
(
LinearBorder
.
none
.
hashCode
,
LinearBorder
.
none
.
copyWith
().
hashCode
);
const
BorderSide
side
=
BorderSide
(
width:
10.0
,
color:
Color
(
0xff123456
));
expect
(
LinearBorder
.
none
.
copyWith
(
side:
side
),
const
LinearBorder
(
side:
side
));
});
test
(
'LinearBorderEdge, LinearBorder toString()'
,
()
{
expect
(
const
LinearBorderEdge
(
size:
0.5
,
alignment:
-
0.5
).
toString
(),
'LinearBorderEdge(size: 0.5, alignment: -0.5)'
);
expect
(
LinearBorder
.
none
.
toString
(),
'LinearBorder.none'
);
const
BorderSide
side
=
BorderSide
(
width:
10.0
,
color:
Color
(
0xff123456
));
expect
(
const
LinearBorder
(
side:
side
).
toString
(),
'LinearBorder(side: BorderSide(color: Color(0xff123456), width: 10.0))'
);
expect
(
const
LinearBorder
(
side:
side
,
start:
LinearBorderEdge
(
size:
0
,
alignment:
-
0.75
),
end:
LinearBorderEdge
(
size:
0.25
,
alignment:
-
0.5
),
top:
LinearBorderEdge
(
size:
0.5
,
alignment:
0.5
),
bottom:
LinearBorderEdge
(
size:
0.75
,
alignment:
0.75
),
).
toString
(),
'LinearBorder('
'side: BorderSide(color: Color(0xff123456), width: 10.0), '
'start: LinearBorderEdge(size: 0.0, alignment: -0.75), '
'end: LinearBorderEdge(size: 0.25, alignment: -0.5), '
'top: LinearBorderEdge(size: 0.5, alignment: 0.5), '
'bottom: LinearBorderEdge(size: 0.75, alignment: 0.75))'
,
);
},
skip:
isBrowser
,
// [intended] see https://github.com/flutter/flutter/issues/118207
);
test
(
'LinearBorder.start()'
,
()
{
final
LinearBorder
border
=
LinearBorder
.
start
(
side:
borderSide
);
expect
(
(
Canvas
canvas
)
=>
border
.
paint
(
canvas
,
canvasRect
,
textDirection:
TextDirection
.
ltr
),
paints
..
path
(
includes:
leftRectIncludes
,
excludes:
rightRectIncludes
,
color:
borderSide
.
color
,
),
);
expect
(
(
Canvas
canvas
)
=>
border
.
paint
(
canvas
,
canvasRect
,
textDirection:
TextDirection
.
rtl
),
paints
..
path
(
includes:
rightRectIncludes
,
excludes:
leftRectIncludes
,
color:
borderSide
.
color
,
),
);
});
test
(
'LinearBorder.end()'
,
()
{
final
LinearBorder
border
=
LinearBorder
.
end
(
side:
borderSide
);
expect
(
(
Canvas
canvas
)
=>
border
.
paint
(
canvas
,
canvasRect
,
textDirection:
TextDirection
.
ltr
),
paints
..
path
(
includes:
rightRectIncludes
,
excludes:
leftRectIncludes
,
color:
borderSide
.
color
,
),
);
expect
(
(
Canvas
canvas
)
=>
border
.
paint
(
canvas
,
canvasRect
,
textDirection:
TextDirection
.
rtl
),
paints
..
path
(
includes:
leftRectIncludes
,
excludes:
rightRectIncludes
,
color:
borderSide
.
color
,
),
);
});
test
(
'LinearBorder.top()'
,
()
{
final
LinearBorder
border
=
LinearBorder
.
top
(
side:
borderSide
);
expect
(
(
Canvas
canvas
)
=>
border
.
paint
(
canvas
,
canvasRect
,
textDirection:
TextDirection
.
ltr
),
paints
..
path
(
includes:
topRectIncludes
,
excludes:
bottomRectIncludes
,
color:
borderSide
.
color
,
),
);
});
test
(
'LinearBorder.bottom()'
,
()
{
final
LinearBorder
border
=
LinearBorder
.
bottom
(
side:
borderSide
);
expect
(
(
Canvas
canvas
)
=>
border
.
paint
(
canvas
,
canvasRect
,
textDirection:
TextDirection
.
ltr
),
paints
..
path
(
includes:
bottomRectIncludes
,
excludes:
topRectIncludes
,
color:
borderSide
.
color
,
),
);
});
}
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