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
07de84f0
Unverified
Commit
07de84f0
authored
Jul 28, 2022
by
Greg Spencer
Committed by
GitHub
Jul 28, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add StarBorder and StarBorder.polygon, with example. (#108489)
parent
e79e6d72
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
1912 additions
and
6 deletions
+1912
-6
analyze.dart
dev/bots/analyze.dart
+1
-1
analyze_test.dart
dev/bots/test/analyze_test.dart
+3
-4
star_border.dart
dev/manual_tests/lib/star_border.dart
+555
-0
star_border.0.dart
examples/api/lib/painting/star_border/star_border.0.dart
+454
-0
painting.dart
packages/flutter/lib/painting.dart
+1
-0
slider.dart
packages/flutter/lib/src/material/slider.dart
+2
-1
star_border.dart
packages/flutter/lib/src/painting/star_border.dart
+698
-0
star_border_test.dart
packages/flutter/test/painting/star_border_test.dart
+198
-0
No files found.
dev/bots/analyze.dart
View file @
07de84f0
...
...
@@ -412,7 +412,7 @@ Future<void> verifyGoldenTags(String workingDirectory, { int minimumMatches = 20
if
(
needsTag
)
{
if
(!
hasTagNotation
)
{
errors
.
add
(
'
${file.path}
: Files containing golden tests must be tagged using '
'`@Tags(...)` at the top of the file before import statements.'
);
"@Tags(<String>['reduced-test-set']) at the top of the file before import statements."
);
}
else
if
(!
hasReducedTag
)
{
errors
.
add
(
'
${file.path}
: Files containing golden tests must be tagged with '
"'reduced-test-set'."
);
...
...
dev/bots/test/analyze_test.dart
View file @
07de84f0
...
...
@@ -74,10 +74,9 @@ void main() {
test
(
'analyze.dart - verifyGoldenTags'
,
()
async
{
final
String
result
=
await
capture
(()
=>
verifyGoldenTags
(
testRootPath
,
minimumMatches:
6
),
exitCode:
1
);
const
String
noTag
=
'Files containing golden tests must be '
'tagged using `@Tags(...)` at the top of the file before import statements.'
;
const
String
missingTag
=
'Files containing golden tests must be '
"tagged with 'reduced-test-set'."
;
const
String
noTag
=
"Files containing golden tests must be tagged using @Tags(<String>['reduced-test-set']) "
'at the top of the file before import statements.'
;
const
String
missingTag
=
"Files containing golden tests must be tagged with 'reduced-test-set'."
;
String
lines
=
<
String
>[
'test/analyze-test-input/root/packages/foo/golden_missing_tag.dart:
$missingTag
'
,
'test/analyze-test-input/root/packages/foo/golden_no_tag.dart:
$noTag
'
,
...
...
dev/manual_tests/lib/star_border.dart
0 → 100644
View file @
07de84f0
// 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/foundation.dart'
;
import
'package:flutter/material.dart'
;
void
main
(
)
=>
runApp
(
const
MyApp
());
enum
LerpTarget
{
circle
,
roundedRect
,
rect
,
stadium
,
polygon
,
star
,
}
class
MyApp
extends
StatelessWidget
{
const
MyApp
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
const
MaterialApp
(
home:
MyHomePage
(),
);
}
}
class
MyHomePage
extends
StatefulWidget
{
const
MyHomePage
({
super
.
key
});
@override
State
<
MyHomePage
>
createState
()
=>
_MyHomePageState
();
}
class
_MyHomePageState
extends
State
<
MyHomePage
>
{
static
final
GlobalKey
<
ScaffoldState
>
scaffoldKey
=
GlobalKey
<
ScaffoldState
>();
final
OptionModel
_model
=
OptionModel
();
final
TextEditingController
textController
=
TextEditingController
();
@override
void
initState
()
{
super
.
initState
();
_model
.
addListener
(
_modelChanged
);
}
@override
void
dispose
()
{
super
.
dispose
();
_model
.
removeListener
(
_modelChanged
);
}
void
_modelChanged
()
{
setState
(()
{});
}
@override
Widget
build
(
BuildContext
context
)
{
return
SafeArea
(
child:
Scaffold
(
key:
scaffoldKey
,
appBar:
AppBar
(
title:
const
Text
(
'Star Border'
),
backgroundColor:
const
Color
(
0xff323232
),
),
body:
Column
(
children:
<
Widget
>[
Container
(
color:
Colors
.
grey
.
shade200
,
child:
Options
(
_model
)),
Expanded
(
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceEvenly
,
children:
<
Widget
>[
Container
(
key:
UniqueKey
(),
alignment:
Alignment
.
center
,
width:
300
,
height:
200
,
decoration:
ShapeDecoration
(
color:
Colors
.
blue
.
shade100
,
shape:
lerpBorder
(
StarBorder
.
polygon
(
side:
const
BorderSide
(
strokeAlign:
StrokeAlign
.
center
,
width:
2
),
sides:
_model
.
points
,
pointRounding:
_model
.
pointRounding
,
rotation:
_model
.
rotation
,
squash:
_model
.
squash
,
),
_model
.
_lerpTarget
,
_model
.
_lerpAmount
,
to:
_model
.
lerpTo
,
)!,
),
child:
const
Text
(
'Polygon'
),
),
Container
(
key:
UniqueKey
(),
alignment:
Alignment
.
center
,
width:
300
,
height:
200
,
decoration:
ShapeDecoration
(
color:
Colors
.
blue
.
shade100
,
shape:
lerpBorder
(
StarBorder
(
side:
const
BorderSide
(
strokeAlign:
StrokeAlign
.
center
,
width:
2
),
points:
_model
.
points
,
innerRadiusRatio:
_model
.
innerRadiusRatio
,
pointRounding:
_model
.
pointRounding
,
valleyRounding:
_model
.
valleyRounding
,
rotation:
_model
.
rotation
,
squash:
_model
.
squash
,
),
_model
.
_lerpTarget
,
_model
.
_lerpAmount
,
to:
_model
.
lerpTo
,
)!,
),
child:
const
Text
(
'Star'
),
),
],
),
),
],
),
),
);
}
}
class
OptionModel
extends
ChangeNotifier
{
double
get
pointRounding
=>
_pointRounding
;
double
_pointRounding
=
0.0
;
set
pointRounding
(
double
value
)
{
if
(
value
!=
_pointRounding
)
{
_pointRounding
=
value
;
if
(
_valleyRounding
+
_pointRounding
>
1
)
{
_valleyRounding
=
1.0
-
_pointRounding
;
}
notifyListeners
();
}
}
double
get
valleyRounding
=>
_valleyRounding
;
double
_valleyRounding
=
0.0
;
set
valleyRounding
(
double
value
)
{
if
(
value
!=
_valleyRounding
)
{
_valleyRounding
=
value
;
if
(
_valleyRounding
+
_pointRounding
>
1
)
{
_pointRounding
=
1.0
-
_valleyRounding
;
}
notifyListeners
();
}
}
double
get
squash
=>
_squash
;
double
_squash
=
0.0
;
set
squash
(
double
value
)
{
if
(
value
!=
_squash
)
{
_squash
=
value
;
notifyListeners
();
}
}
double
get
rotation
=>
_rotation
;
double
_rotation
=
0.0
;
set
rotation
(
double
value
)
{
if
(
value
!=
_rotation
)
{
_rotation
=
value
;
notifyListeners
();
}
}
double
get
innerRadiusRatio
=>
_innerRadiusRatio
;
double
_innerRadiusRatio
=
0.4
;
set
innerRadiusRatio
(
double
value
)
{
if
(
value
!=
_innerRadiusRatio
)
{
_innerRadiusRatio
=
clampDouble
(
value
,
0.0001
,
double
.
infinity
);
notifyListeners
();
}
}
double
get
points
=>
_points
;
double
_points
=
5
;
set
points
(
double
value
)
{
if
(
value
!=
_points
)
{
_points
=
value
;
notifyListeners
();
}
}
double
get
lerpAmount
=>
_lerpAmount
;
double
_lerpAmount
=
0.0
;
set
lerpAmount
(
double
value
)
{
if
(
value
!=
_lerpAmount
)
{
_lerpAmount
=
value
;
notifyListeners
();
}
}
bool
get
lerpTo
=>
_lerpTo
;
bool
_lerpTo
=
true
;
set
lerpTo
(
bool
value
)
{
if
(
_lerpTo
!=
value
)
{
_lerpTo
=
value
;
notifyListeners
();
}
}
LerpTarget
get
lerpTarget
=>
_lerpTarget
;
LerpTarget
_lerpTarget
=
LerpTarget
.
circle
;
set
lerpTarget
(
LerpTarget
value
)
{
if
(
value
!=
_lerpTarget
)
{
_lerpTarget
=
value
;
notifyListeners
();
}
}
void
reset
()
{
final
OptionModel
defaultModel
=
OptionModel
();
_pointRounding
=
defaultModel
.
pointRounding
;
_valleyRounding
=
defaultModel
.
valleyRounding
;
_rotation
=
defaultModel
.
rotation
;
_squash
=
defaultModel
.
squash
;
_lerpAmount
=
defaultModel
.
lerpAmount
;
_lerpTo
=
defaultModel
.
lerpTo
;
_lerpTarget
=
defaultModel
.
lerpTarget
;
_innerRadiusRatio
=
defaultModel
.
_innerRadiusRatio
;
_points
=
defaultModel
.
points
;
notifyListeners
();
}
}
class
LabeledCheckbox
extends
StatelessWidget
{
const
LabeledCheckbox
({
super
.
key
,
required
this
.
label
,
this
.
onChanged
,
this
.
value
});
final
String
label
;
final
ValueChanged
<
bool
?>?
onChanged
;
final
bool
?
value
;
@override
Widget
build
(
BuildContext
context
)
{
return
Row
(
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
Checkbox
(
onChanged:
onChanged
,
value:
value
,
),
Text
(
label
),
],
);
}
}
class
Options
extends
StatefulWidget
{
const
Options
(
this
.
model
,
{
super
.
key
});
final
OptionModel
model
;
@override
State
<
Options
>
createState
()
=>
_OptionsState
();
}
class
_OptionsState
extends
State
<
Options
>
{
@override
void
initState
()
{
super
.
initState
();
widget
.
model
.
addListener
(
_modelChanged
);
}
@override
void
didUpdateWidget
(
Options
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
if
(
widget
.
model
!=
oldWidget
.
model
)
{
oldWidget
.
model
.
removeListener
(
_modelChanged
);
widget
.
model
.
addListener
(
_modelChanged
);
}
}
@override
void
dispose
()
{
super
.
dispose
();
widget
.
model
.
removeListener
(
_modelChanged
);
}
void
_modelChanged
()
{
setState
(()
{});
}
double
sliderValue
=
0.0
;
@override
Widget
build
(
BuildContext
context
)
{
return
Padding
(
padding:
const
EdgeInsets
.
fromLTRB
(
5.0
,
0.0
,
5.0
,
10.0
),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
Row
(
children:
<
Widget
>[
Expanded
(
child:
ControlSlider
(
label:
'Point Rounding'
,
value:
widget
.
model
.
pointRounding
,
onChanged:
(
double
value
)
{
widget
.
model
.
pointRounding
=
value
;
},
),
),
Expanded
(
child:
ControlSlider
(
label:
'Valley Rounding'
,
value:
widget
.
model
.
valleyRounding
,
onChanged:
(
double
value
)
{
widget
.
model
.
valleyRounding
=
value
;
},
),
),
],
),
Row
(
children:
<
Widget
>[
Expanded
(
child:
ControlSlider
(
label:
'Squash'
,
value:
widget
.
model
.
squash
,
onChanged:
(
double
value
)
{
widget
.
model
.
squash
=
value
;
},
),
),
Expanded
(
child:
ControlSlider
(
label:
'Rotation'
,
value:
widget
.
model
.
rotation
,
max:
360
,
onChanged:
(
double
value
)
{
widget
.
model
.
rotation
=
value
;
},
),
),
],
),
Row
(
children:
<
Widget
>[
Expanded
(
child:
Row
(
children:
<
Widget
>[
Expanded
(
child:
ControlSlider
(
label:
'Points'
,
value:
widget
.
model
.
points
,
min:
2
,
max:
20
,
onChanged:
(
double
value
)
{
widget
.
model
.
points
=
value
;
},
),
),
OutlinedButton
(
child:
const
Text
(
'Nearest'
),
onPressed:
()
{
widget
.
model
.
points
=
widget
.
model
.
points
.
roundToDouble
();
}),
],
),
),
Expanded
(
child:
ControlSlider
(
label:
'Inner Radius'
,
value:
widget
.
model
.
innerRadiusRatio
,
onChanged:
(
double
value
)
{
widget
.
model
.
innerRadiusRatio
=
value
;
},
),
),
],
),
Row
(
children:
<
Widget
>[
Expanded
(
flex:
2
,
child:
Padding
(
padding:
const
EdgeInsetsDirectional
.
only
(
end:
8.0
),
child:
ControlSlider
(
label:
'Lerp'
,
value:
widget
.
model
.
lerpAmount
,
onChanged:
(
double
value
)
{
widget
.
model
.
lerpAmount
=
value
;
},
),
),
),
Padding
(
padding:
const
EdgeInsetsDirectional
.
only
(
start:
8.0
,
end:
20.0
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
Row
(
children:
<
Widget
>[
Radio
<
bool
>(
value:
true
,
groupValue:
widget
.
model
.
lerpTo
,
onChanged:
(
bool
?
value
)
{
widget
.
model
.
lerpTo
=
value
!;
}),
const
Text
(
'To'
),
]),
Row
(
children:
<
Widget
>[
Radio
<
bool
>(
value:
false
,
groupValue:
widget
.
model
.
lerpTo
,
onChanged:
(
bool
?
value
)
{
widget
.
model
.
lerpTo
=
value
!;
}),
const
Text
(
'From'
),
])
],
),
),
Expanded
(
child:
Row
(
children:
<
Widget
>[
Expanded
(
child:
DropdownButton
<
LerpTarget
>(
items:
LerpTarget
.
values
.
map
<
DropdownMenuItem
<
LerpTarget
>>((
LerpTarget
target
)
{
return
DropdownMenuItem
<
LerpTarget
>(
value:
target
,
child:
Text
(
target
.
name
));
}).
toList
(),
value:
widget
.
model
.
lerpTarget
,
onChanged:
(
LerpTarget
?
value
)
{
if
(
value
==
null
)
{
return
;
}
widget
.
model
.
lerpTarget
=
value
;
},
),
),
],
),
),
],
),
ElevatedButton
(
onPressed:
()
{
widget
.
model
.
reset
();
sliderValue
=
0.0
;
},
child:
const
Text
(
'Reset'
),
),
],
),
);
}
}
class
ControlSlider
extends
StatelessWidget
{
const
ControlSlider
({
super
.
key
,
required
this
.
label
,
required
this
.
value
,
required
this
.
onChanged
,
this
.
min
=
0.0
,
this
.
max
=
1.0
,
});
final
String
label
;
final
double
value
;
final
void
Function
(
double
value
)
onChanged
;
final
double
min
;
final
double
max
;
@override
Widget
build
(
BuildContext
context
)
{
return
Padding
(
padding:
const
EdgeInsets
.
all
(
4.0
),
child:
Row
(
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
Text
(
label
),
Expanded
(
child:
Slider
(
label:
value
.
toStringAsFixed
(
1
),
onChanged:
onChanged
,
min:
min
,
max:
max
,
value:
value
,
),
),
Text
(
value
.
toStringAsFixed
(
3
),
),
],
),
);
}
}
const
Color
lerpToColor
=
Colors
.
red
;
const
BorderSide
lerpToBorder
=
BorderSide
(
width:
5
,
color:
lerpToColor
);
ShapeBorder
?
lerpBorder
(
StarBorder
border
,
LerpTarget
target
,
double
t
,
{
bool
to
=
true
})
{
switch
(
target
)
{
case
LerpTarget
.
circle
:
if
(
to
)
{
return
border
.
lerpTo
(
const
CircleBorder
(
side:
lerpToBorder
),
t
);
}
else
{
return
border
.
lerpFrom
(
const
CircleBorder
(
side:
lerpToBorder
),
t
);
}
case
LerpTarget
.
roundedRect
:
if
(
to
)
{
return
border
.
lerpTo
(
const
RoundedRectangleBorder
(
side:
lerpToBorder
,
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
10
),
),
),
t
,
);
}
else
{
return
border
.
lerpFrom
(
const
RoundedRectangleBorder
(
side:
lerpToBorder
,
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
10
),
),
),
t
,
);
}
case
LerpTarget
.
rect
:
if
(
to
)
{
return
border
.
lerpTo
(
const
RoundedRectangleBorder
(
side:
lerpToBorder
),
t
);
}
else
{
return
border
.
lerpFrom
(
const
RoundedRectangleBorder
(
side:
lerpToBorder
),
t
);
}
case
LerpTarget
.
stadium
:
if
(
to
)
{
return
border
.
lerpTo
(
const
StadiumBorder
(
side:
lerpToBorder
),
t
);
}
else
{
return
border
.
lerpFrom
(
const
StadiumBorder
(
side:
lerpToBorder
),
t
);
}
case
LerpTarget
.
polygon
:
if
(
to
)
{
return
border
.
lerpTo
(
const
StarBorder
.
polygon
(
side:
lerpToBorder
,
sides:
4
),
t
);
}
else
{
return
border
.
lerpFrom
(
const
StarBorder
.
polygon
(
side:
lerpToBorder
,
sides:
4
),
t
);
}
case
LerpTarget
.
star
:
if
(
to
)
{
return
border
.
lerpTo
(
const
StarBorder
(
side:
lerpToBorder
,
innerRadiusRatio:
.
5
),
t
);
}
else
{
return
border
.
lerpFrom
(
const
StarBorder
(
side:
lerpToBorder
,
innerRadiusRatio:
.
5
),
t
);
}
}
}
examples/api/lib/painting/star_border/star_border.0.dart
0 → 100644
View file @
07de84f0
// 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.
/// An example showing usage of [StarBorder].
import
'package:flutter/material.dart'
;
const
int
_kParameterPrecision
=
2
;
void
main
(
)
=>
runApp
(
const
StarBorderApp
());
class
StarBorderApp
extends
StatelessWidget
{
const
StarBorderApp
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
MaterialApp
(
home:
Scaffold
(
appBar:
AppBar
(
title:
const
Text
(
'StarBorder Example'
),
backgroundColor:
const
Color
(
0xff323232
),
),
body:
const
StarBorderExample
(),
),
);
}
}
class
StarBorderExample
extends
StatefulWidget
{
const
StarBorderExample
({
super
.
key
});
@override
State
<
StarBorderExample
>
createState
()
=>
_StarBorderExampleState
();
}
class
_StarBorderExampleState
extends
State
<
StarBorderExample
>
{
final
OptionModel
_model
=
OptionModel
();
final
TextEditingController
_textController
=
TextEditingController
();
@override
void
initState
()
{
super
.
initState
();
_model
.
addListener
(
_modelChanged
);
}
@override
void
dispose
()
{
_model
.
removeListener
(
_modelChanged
);
_textController
.
dispose
();
super
.
dispose
();
}
void
_modelChanged
()
{
setState
(()
{});
}
@override
Widget
build
(
BuildContext
context
)
{
return
DefaultTextStyle
(
style:
const
TextStyle
(
color:
Colors
.
black
,
fontSize:
14.0
,
fontFamily:
'Roboto'
,
fontStyle:
FontStyle
.
normal
,
),
child:
ListView
(
children:
<
Widget
>[
Container
(
color:
Colors
.
grey
.
shade200
,
child:
Options
(
_model
),
),
Padding
(
padding:
const
EdgeInsets
.
all
(
18.0
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceEvenly
,
children:
<
Widget
>[
Expanded
(
child:
ExampleBorder
(
border:
StarBorder
(
side:
const
BorderSide
(),
points:
_model
.
points
,
innerRadiusRatio:
_model
.
innerRadiusRatio
,
pointRounding:
_model
.
pointRounding
,
valleyRounding:
_model
.
valleyRounding
,
rotation:
_model
.
rotation
,
squash:
_model
.
squash
,
),
title:
'Star'
,
),
),
Expanded
(
child:
ExampleBorder
(
border:
StarBorder
.
polygon
(
side:
const
BorderSide
(),
sides:
_model
.
points
,
pointRounding:
_model
.
pointRounding
,
rotation:
_model
.
rotation
,
squash:
_model
.
squash
,
),
title:
'Polygon'
,
),
),
],
),
),
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
Expanded
(
child:
Container
(
color:
Colors
.
black12
,
margin:
const
EdgeInsets
.
all
(
16.0
),
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
SelectableText
(
_model
.
starCode
),
),
),
Expanded
(
child:
Container
(
color:
Colors
.
black12
,
margin:
const
EdgeInsets
.
all
(
16.0
),
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
SelectableText
(
_model
.
polygonCode
),
),
),
],
),
],
),
);
}
}
class
ExampleBorder
extends
StatelessWidget
{
const
ExampleBorder
({
super
.
key
,
required
this
.
border
,
required
this
.
title
,
});
final
StarBorder
border
;
final
String
title
;
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
key:
UniqueKey
(),
alignment:
Alignment
.
center
,
padding:
const
EdgeInsets
.
all
(
20
),
width:
150
,
height:
100
,
decoration:
ShapeDecoration
(
color:
Colors
.
blue
.
shade100
,
shape:
border
,
),
child:
Text
(
title
),
);
}
}
class
Options
extends
StatefulWidget
{
const
Options
(
this
.
model
,
{
super
.
key
});
final
OptionModel
model
;
@override
State
<
Options
>
createState
()
=>
_OptionsState
();
}
class
_OptionsState
extends
State
<
Options
>
{
@override
void
initState
()
{
super
.
initState
();
widget
.
model
.
addListener
(
_modelChanged
);
}
@override
void
didUpdateWidget
(
Options
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
if
(
widget
.
model
!=
oldWidget
.
model
)
{
oldWidget
.
model
.
removeListener
(
_modelChanged
);
widget
.
model
.
addListener
(
_modelChanged
);
}
}
@override
void
dispose
()
{
super
.
dispose
();
widget
.
model
.
removeListener
(
_modelChanged
);
}
void
_modelChanged
()
{
setState
(()
{});
}
@override
Widget
build
(
BuildContext
context
)
{
return
Padding
(
padding:
const
EdgeInsets
.
fromLTRB
(
5.0
,
0.0
,
5.0
,
10.0
),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
Row
(
children:
<
Widget
>[
Expanded
(
child:
ControlSlider
(
label:
'Point Rounding'
,
value:
widget
.
model
.
pointRounding
,
onChanged:
(
double
value
)
{
widget
.
model
.
pointRounding
=
value
;
},
),
),
Expanded
(
child:
ControlSlider
(
label:
'Valley Rounding'
,
value:
widget
.
model
.
valleyRounding
,
onChanged:
(
double
value
)
{
widget
.
model
.
valleyRounding
=
value
;
},
),
),
],
),
Row
(
children:
<
Widget
>[
Expanded
(
child:
ControlSlider
(
label:
'Squash'
,
value:
widget
.
model
.
squash
,
onChanged:
(
double
value
)
{
widget
.
model
.
squash
=
value
;
},
),
),
Expanded
(
child:
ControlSlider
(
label:
'Rotation'
,
value:
widget
.
model
.
rotation
,
max:
360
,
onChanged:
(
double
value
)
{
widget
.
model
.
rotation
=
value
;
},
),
),
],
),
Row
(
children:
<
Widget
>[
Expanded
(
child:
Row
(
children:
<
Widget
>[
Expanded
(
child:
ControlSlider
(
label:
'Points'
,
value:
widget
.
model
.
points
,
min:
3
,
max:
20
,
precision:
1
,
onChanged:
(
double
value
)
{
widget
.
model
.
points
=
value
;
},
),
),
Tooltip
(
message:
'Round the number of points to the nearest integer.'
,
child:
Padding
(
padding:
const
EdgeInsets
.
all
(
8.0
),
child:
OutlinedButton
(
child:
const
Text
(
'Nearest'
),
onPressed:
()
{
widget
.
model
.
points
=
widget
.
model
.
points
.
roundToDouble
();
},
),
),
),
],
),
),
Expanded
(
child:
ControlSlider
(
label:
'Inner Radius'
,
value:
widget
.
model
.
innerRadiusRatio
,
onChanged:
(
double
value
)
{
widget
.
model
.
innerRadiusRatio
=
value
;
},
),
),
],
),
ElevatedButton
(
onPressed:
()
{
widget
.
model
.
reset
();
},
child:
const
Text
(
'Reset'
),
),
],
),
);
}
}
class
OptionModel
extends
ChangeNotifier
{
double
get
pointRounding
=>
_pointRounding
;
double
_pointRounding
=
0.0
;
set
pointRounding
(
double
value
)
{
if
(
value
!=
_pointRounding
)
{
_pointRounding
=
value
;
if
(
_valleyRounding
+
_pointRounding
>
1
)
{
_valleyRounding
=
1.0
-
_pointRounding
;
}
notifyListeners
();
}
}
double
get
valleyRounding
=>
_valleyRounding
;
double
_valleyRounding
=
0.0
;
set
valleyRounding
(
double
value
)
{
if
(
value
!=
_valleyRounding
)
{
_valleyRounding
=
value
;
if
(
_valleyRounding
+
_pointRounding
>
1
)
{
_pointRounding
=
1.0
-
_valleyRounding
;
}
notifyListeners
();
}
}
double
get
squash
=>
_squash
;
double
_squash
=
0.0
;
set
squash
(
double
value
)
{
if
(
value
!=
_squash
)
{
_squash
=
value
;
notifyListeners
();
}
}
double
get
rotation
=>
_rotation
;
double
_rotation
=
0.0
;
set
rotation
(
double
value
)
{
if
(
value
!=
_rotation
)
{
_rotation
=
value
;
notifyListeners
();
}
}
double
get
innerRadiusRatio
=>
_innerRadiusRatio
;
double
_innerRadiusRatio
=
0.4
;
set
innerRadiusRatio
(
double
value
)
{
if
(
value
!=
_innerRadiusRatio
)
{
_innerRadiusRatio
=
value
.
clamp
(
0.0001
,
double
.
infinity
);
notifyListeners
();
}
}
double
get
points
=>
_points
;
double
_points
=
5
;
set
points
(
double
value
)
{
if
(
value
!=
_points
)
{
_points
=
value
;
notifyListeners
();
}
}
String
get
starCode
{
return
'Container(
\n
'
' decoration: ShapeDecoration(
\n
'
' shape: StarBorder(
\n
'
' points:
${points.toStringAsFixed(_kParameterPrecision)}
,
\n
'
' rotation:
${rotation.toStringAsFixed(_kParameterPrecision)}
,
\n
'
' innerRadiusRatio:
${innerRadiusRatio.toStringAsFixed(_kParameterPrecision)}
,
\n
'
' pointRounding:
${pointRounding.toStringAsFixed(_kParameterPrecision)}
,
\n
'
' valleyRounding:
${valleyRounding.toStringAsFixed(_kParameterPrecision)}
,
\n
'
' squash:
${squash.toStringAsFixed(_kParameterPrecision)}
,
\n
'
' ),
\n
'
' ),
\n
'
');'
;
}
String
get
polygonCode
{
return
'Container(
\n
'
' decoration: ShapeDecoration(
\n
'
' shape: StarBorder.polygon(
\n
'
' sides:
${points.toStringAsFixed(_kParameterPrecision)}
,
\n
'
' rotation:
${rotation.toStringAsFixed(_kParameterPrecision)}
,
\n
'
' cornerRounding:
${pointRounding.toStringAsFixed(_kParameterPrecision)}
,
\n
'
' squash:
${squash.toStringAsFixed(_kParameterPrecision)}
,
\n
'
' ),
\n
'
' ),
\n
'
');'
;
}
void
reset
()
{
final
OptionModel
defaultModel
=
OptionModel
();
_pointRounding
=
defaultModel
.
pointRounding
;
_valleyRounding
=
defaultModel
.
valleyRounding
;
_rotation
=
defaultModel
.
rotation
;
_squash
=
defaultModel
.
squash
;
_innerRadiusRatio
=
defaultModel
.
_innerRadiusRatio
;
_points
=
defaultModel
.
points
;
notifyListeners
();
}
}
class
ControlSlider
extends
StatelessWidget
{
const
ControlSlider
({
super
.
key
,
required
this
.
label
,
required
this
.
value
,
required
this
.
onChanged
,
this
.
min
=
0.0
,
this
.
max
=
1.0
,
this
.
precision
=
_kParameterPrecision
,
});
final
String
label
;
final
double
value
;
final
void
Function
(
double
value
)
onChanged
;
final
double
min
;
final
double
max
;
final
int
precision
;
@override
Widget
build
(
BuildContext
context
)
{
return
Padding
(
padding:
const
EdgeInsets
.
all
(
4.0
),
child:
Row
(
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
Expanded
(
flex:
2
,
child:
Text
(
label
,
textAlign:
TextAlign
.
end
,
),
),
Expanded
(
flex:
5
,
child:
Slider
(
onChanged:
onChanged
,
min:
min
,
max:
max
,
value:
value
,
),
),
Expanded
(
child:
Text
(
value
.
toStringAsFixed
(
precision
),
),
),
],
),
);
}
}
packages/flutter/lib/painting.dart
View file @
07de84f0
...
...
@@ -56,6 +56,7 @@ export 'src/painting/rounded_rectangle_border.dart';
export
'src/painting/shader_warm_up.dart'
;
export
'src/painting/shape_decoration.dart'
;
export
'src/painting/stadium_border.dart'
;
export
'src/painting/star_border.dart'
;
export
'src/painting/strut_style.dart'
;
export
'src/painting/text_painter.dart'
;
export
'src/painting/text_span.dart'
;
...
...
packages/flutter/lib/src/material/slider.dart
View file @
07de84f0
...
...
@@ -144,7 +144,8 @@ class Slider extends StatefulWidget {
assert
(
min
!=
null
),
assert
(
max
!=
null
),
assert
(
min
<=
max
),
assert
(
value
>=
min
&&
value
<=
max
),
assert
(
value
>=
min
&&
value
<=
max
,
'Value
$value
is not between minimum
$min
and maximum
$max
'
),
assert
(
divisions
==
null
||
divisions
>
0
);
/// Creates an adaptive [Slider] based on the target platform, following
...
...
packages/flutter/lib/src/painting/star_border.dart
0 → 100644
View file @
07de84f0
// 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'
as
ui
show
lerpDouble
;
import
'package:flutter/foundation.dart'
;
import
'package:vector_math/vector_math_64.dart'
show
Matrix4
;
import
'basic_types.dart'
;
import
'borders.dart'
;
import
'circle_border.dart'
;
import
'edge_insets.dart'
;
import
'rounded_rectangle_border.dart'
;
import
'stadium_border.dart'
;
// Conversion from radians to degrees.
const
double
_kRadToDeg
=
180
/
math
.
pi
;
// Conversion from degrees to radians.
const
double
_kDegToRad
=
math
.
pi
/
180
;
/// A border that fits a star or polygon-shaped border within the rectangle of
/// the widget it is applied to.
///
/// Typically used with a [ShapeDecoration] to draw a polygonal or star shaped
/// border.
///
/// {@tool dartpad}
/// This example serves both as a usage example, as well as an explorer for
/// determining the parameters to use with a [StarBorder]. The resulting code
/// can be copied and pasted into your app. A [Container] is just one widget
/// which takes a [ShapeBorder]. [Dialog]s, [OutlinedButton]s,
/// [ElevatedButton]s, etc. all can be shaped with a [ShapeBorder].
///
/// ** See code in examples/api/lib/painting/star_border/star_border.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [BorderSide], which is used to describe how the edge of the shape is
/// drawn.
class
StarBorder
extends
OutlinedBorder
{
/// Create a const star-shaped border with the given number [points] on the
/// star.
const
StarBorder
({
super
.
side
,
this
.
points
=
5
,
double
innerRadiusRatio
=
0.4
,
this
.
pointRounding
=
0
,
this
.
valleyRounding
=
0
,
double
rotation
=
0
,
this
.
squash
=
0
,
})
:
assert
(
squash
>=
0
),
assert
(
squash
<=
1
),
assert
(
pointRounding
>=
0
),
assert
(
pointRounding
<=
1
),
assert
(
valleyRounding
>=
0
),
assert
(
valleyRounding
<=
1
),
assert
(
(
valleyRounding
+
pointRounding
)
<=
1
,
'The sum of valleyRounding (
$valleyRounding
) and '
'pointRounding (
$pointRounding
) must not exceed one.'
),
assert
(
innerRadiusRatio
>=
0
),
assert
(
innerRadiusRatio
<=
1
),
assert
(
points
>=
2
),
_rotationRadians
=
rotation
*
_kDegToRad
,
_innerRadiusRatio
=
innerRadiusRatio
;
/// Create a const polygon border with the given number of [sides].
const
StarBorder
.
polygon
({
super
.
side
,
double
sides
=
5
,
this
.
pointRounding
=
0
,
double
rotation
=
0
,
this
.
squash
=
0
,
})
:
assert
(
squash
>=
0
),
assert
(
squash
<=
1
),
assert
(
pointRounding
>=
0
),
assert
(
pointRounding
<=
1
),
assert
(
sides
>=
2
),
points
=
sides
,
valleyRounding
=
0
,
_rotationRadians
=
rotation
*
_kDegToRad
,
_innerRadiusRatio
=
null
;
/// The number of points in this star, or sides on a polygon.
///
/// This is a floating point number: if this is not a whole number, then an
/// additional star point or corner shorter than the others will be added to
/// finish the shape. Only whole-numbered values will yield a symmetric shape.
///
/// For stars created with [StarBorder], this the number of points on
/// the star. For polygons created with [StarBorder.polygon], this is the
/// number of sides on the polygon.
///
/// Must be greater than or equal to two.
final
double
points
;
/// The ratio of the inner radius of a star with the outer radius.
///
/// When making a star using [StarBorder], this is the ratio of the inner
/// radius that to the outer radius. If it is one, then the inner radius
/// will equal the outer radius.
///
/// For polygons created with [StarBorder.polygon], getting this value will
/// return the incircle radius of the polygon (the radius of a circle
/// inscribed inside the polygon).
///
/// Defaults to 0.4 for stars, and must be between zero and one, inclusive.
double
get
innerRadiusRatio
{
// Polygons are just a special case of a star where the inner radius is the
// incircle radius of the polygon (the radius of an inscribed circle).
return
_innerRadiusRatio
??
math
.
cos
(
math
.
pi
/
points
);
}
final
double
?
_innerRadiusRatio
;
/// The amount of rounding on the points of stars, or the corners of polygons.
///
/// This is a value between zero and one which describes how rounded the point
/// or corner should be. A value of zero means no rounding (sharp corners),
/// and a value of one means that the entire point or corner is a portion of a
/// circle.
///
/// Defaults to zero. The sum of `pointRounding` and [valleyRounding] must be
/// less than or equal to one.
final
double
pointRounding
;
/// The amount of rounding of the interior corners of stars.
///
/// This is a value between zero and one which describes how rounded the inner
/// corners in a star (the "valley" between points) should be. A value of zero
/// means no rounding (sharp corners), and a value of one means that the
/// entire corner is a portion of a circle.
///
/// Defaults to zero. The sum of [pointRounding] and `valleyRounding` must be
/// less than or equal to one. For polygons created with [StarBorder.polygon],
/// this will always be zero.
final
double
valleyRounding
;
/// The rotation in clockwise degrees around the center of the shape.
///
/// The rotation occurs before the [squash] effect is applied, so that you can
/// fine tune where the points of a star or corners of a polygon start.
///
/// Defaults to zero, meaning that the first point or corner is pointing up.
double
get
rotation
=>
_rotationRadians
*
_kRadToDeg
;
final
double
_rotationRadians
;
/// How much of the aspect ratio of the attached widget to take on.
///
/// If `squash` is non-zero, the border will match the aspect ratio of the
/// bounding box of the widget that it is attached to, which can give a
/// squashed appearance.
///
/// The `squash` parameter lets you control how much of that aspect ratio this
/// border takes on.
///
/// A value of zero means that the border will be drawn with a square aspect
/// ratio at the size of the shortest side of the bounding rectangle, ignoring
/// the aspect ratio of the widget, and a value of one means it will be drawn
/// with the aspect ratio of the widget. The value of `squash` has no effect
/// if the widget is square to begin with.
///
/// Defaults to zero, and must be between zero and one, inclusive.
final
double
squash
;
@override
EdgeInsetsGeometry
get
dimensions
{
switch
(
side
.
strokeAlign
)
{
case
StrokeAlign
.
inside
:
return
EdgeInsets
.
all
(
side
.
width
);
case
StrokeAlign
.
center
:
return
EdgeInsets
.
all
(
side
.
width
/
2
);
case
StrokeAlign
.
outside
:
return
EdgeInsets
.
zero
;
}
}
@override
ShapeBorder
scale
(
double
t
)
{
return
StarBorder
(
points:
points
,
side:
side
.
scale
(
t
),
rotation:
rotation
,
innerRadiusRatio:
innerRadiusRatio
,
pointRounding:
pointRounding
,
valleyRounding:
valleyRounding
,
squash:
squash
,
);
}
ShapeBorder
?
_twoPhaseLerp
(
double
t
,
double
split
,
ShapeBorder
?
Function
(
double
t
)
first
,
ShapeBorder
?
Function
(
double
t
)
second
,
)
{
// If the rectangle has square corners, then skip the extra lerp to round the corners.
if
(
t
<
split
)
{
return
first
(
t
*
(
1
/
split
));
}
else
{
t
=
(
1
/
(
1.0
-
split
))
*
(
t
-
split
);
return
second
(
t
);
}
}
@override
ShapeBorder
?
lerpFrom
(
ShapeBorder
?
a
,
double
t
)
{
if
(
t
==
0
)
{
return
a
;
}
if
(
t
==
1.0
)
{
return
this
;
}
if
(
a
is
StarBorder
)
{
return
StarBorder
(
side:
BorderSide
.
lerp
(
a
.
side
,
side
,
t
),
points:
ui
.
lerpDouble
(
a
.
points
,
points
,
t
)!,
rotation:
ui
.
lerpDouble
(
a
.
_rotationRadians
,
_rotationRadians
,
t
)!
*
_kRadToDeg
,
innerRadiusRatio:
ui
.
lerpDouble
(
a
.
innerRadiusRatio
,
innerRadiusRatio
,
t
)!,
pointRounding:
ui
.
lerpDouble
(
a
.
pointRounding
,
pointRounding
,
t
)!,
valleyRounding:
ui
.
lerpDouble
(
a
.
valleyRounding
,
valleyRounding
,
t
)!,
squash:
ui
.
lerpDouble
(
a
.
squash
,
squash
,
t
)!,
);
}
if
(
a
is
CircleBorder
)
{
if
(
points
>=
2.5
)
{
final
double
lerpedPoints
=
ui
.
lerpDouble
(
points
.
round
(),
points
,
t
)!;
return
StarBorder
(
side:
BorderSide
.
lerp
(
a
.
side
,
side
,
t
),
points:
lerpedPoints
,
rotation:
rotation
,
innerRadiusRatio:
ui
.
lerpDouble
(
math
.
cos
(
math
.
pi
/
lerpedPoints
),
innerRadiusRatio
,
t
)!,
pointRounding:
ui
.
lerpDouble
(
1.0
,
pointRounding
,
t
)!,
valleyRounding:
ui
.
lerpDouble
(
0.0
,
valleyRounding
,
t
)!,
);
}
else
{
// Have a slightly different lerp for two-pointed stars, since they get
// kind of squirrelly with near-zero innerRadiusRatios.
final
double
lerpedPoints
=
ui
.
lerpDouble
(
points
,
2
,
t
)!;
return
StarBorder
(
side:
BorderSide
.
lerp
(
a
.
side
,
side
,
t
),
points:
lerpedPoints
,
rotation:
rotation
,
innerRadiusRatio:
ui
.
lerpDouble
(
1
,
innerRadiusRatio
,
t
)!,
pointRounding:
ui
.
lerpDouble
(
0.5
,
pointRounding
,
t
)!,
valleyRounding:
ui
.
lerpDouble
(
0.5
,
valleyRounding
,
t
)!,
);
}
}
if
(
a
is
StadiumBorder
)
{
// Lerp from a stadium to a circle first, and from there to a star.
final
BorderSide
lerpedSide
=
BorderSide
.
lerp
(
a
.
side
,
side
,
t
);
return
_twoPhaseLerp
(
t
,
0.5
,
(
double
t
)
=>
a
.
lerpTo
(
CircleBorder
(
side:
lerpedSide
),
t
),
(
double
t
)
=>
lerpFrom
(
CircleBorder
(
side:
lerpedSide
),
t
),
);
}
if
(
a
is
RoundedRectangleBorder
)
{
// Lerp from a rectangle to a stadium, then from a Stadium to a circle,
// then from a circle to a star.
final
BorderSide
lerpedSide
=
BorderSide
.
lerp
(
a
.
side
,
side
,
t
);
return
_twoPhaseLerp
(
t
,
1
/
3
,
(
double
t
)
{
return
StadiumBorder
(
side:
lerpedSide
).
lerpFrom
(
a
,
t
);
},
(
double
t
)
{
return
_twoPhaseLerp
(
t
,
0.5
,
(
double
t
)
=>
StadiumBorder
(
side:
lerpedSide
).
lerpTo
(
CircleBorder
(
side:
lerpedSide
),
t
),
(
double
t
)
=>
lerpFrom
(
CircleBorder
(
side:
lerpedSide
),
t
),
);
},
);
}
return
super
.
lerpFrom
(
a
,
t
);
}
@override
ShapeBorder
?
lerpTo
(
ShapeBorder
?
b
,
double
t
)
{
if
(
t
==
0
)
{
return
this
;
}
if
(
t
==
1.0
)
{
return
b
;
}
if
(
b
is
StarBorder
)
{
return
StarBorder
(
side:
BorderSide
.
lerp
(
side
,
b
.
side
,
t
),
points:
ui
.
lerpDouble
(
points
,
b
.
points
,
t
)!,
rotation:
ui
.
lerpDouble
(
_rotationRadians
,
b
.
_rotationRadians
,
t
)!
*
_kRadToDeg
,
innerRadiusRatio:
ui
.
lerpDouble
(
innerRadiusRatio
,
b
.
innerRadiusRatio
,
t
)!,
pointRounding:
ui
.
lerpDouble
(
pointRounding
,
b
.
pointRounding
,
t
)!,
valleyRounding:
ui
.
lerpDouble
(
valleyRounding
,
b
.
valleyRounding
,
t
)!,
squash:
ui
.
lerpDouble
(
squash
,
b
.
squash
,
t
)!,
);
}
if
(
b
is
CircleBorder
)
{
// Have a slightly different lerp for two-pointed stars, since they get
// kind of squirrelly with near-zero innerRadiusRatios.
if
(
points
>=
2.5
)
{
final
double
lerpedPoints
=
ui
.
lerpDouble
(
points
,
points
.
round
(),
t
)!;
return
StarBorder
(
side:
BorderSide
.
lerp
(
side
,
b
.
side
,
t
),
points:
lerpedPoints
,
rotation:
rotation
,
innerRadiusRatio:
ui
.
lerpDouble
(
innerRadiusRatio
,
math
.
cos
(
math
.
pi
/
lerpedPoints
),
t
)!,
pointRounding:
ui
.
lerpDouble
(
pointRounding
,
1.0
,
t
)!,
valleyRounding:
ui
.
lerpDouble
(
valleyRounding
,
0.0
,
t
)!,
);
}
else
{
final
double
lerpedPoints
=
ui
.
lerpDouble
(
points
,
2
,
t
)!;
return
StarBorder
(
side:
BorderSide
.
lerp
(
side
,
b
.
side
,
t
),
points:
lerpedPoints
,
rotation:
rotation
,
innerRadiusRatio:
ui
.
lerpDouble
(
innerRadiusRatio
,
1
,
t
)!,
pointRounding:
ui
.
lerpDouble
(
pointRounding
,
0.5
,
t
)!,
valleyRounding:
ui
.
lerpDouble
(
valleyRounding
,
0.5
,
t
)!,
);
}
}
if
(
b
is
StadiumBorder
)
{
// Lerp to a circle first, then to a stadium.
final
BorderSide
lerpedSide
=
BorderSide
.
lerp
(
side
,
b
.
side
,
t
);
return
_twoPhaseLerp
(
t
,
0.5
,
(
double
t
)
=>
lerpTo
(
CircleBorder
(
side:
lerpedSide
),
t
),
(
double
t
)
=>
b
.
lerpFrom
(
CircleBorder
(
side:
lerpedSide
),
t
),
);
}
if
(
b
is
RoundedRectangleBorder
)
{
// Lerp to a circle, and then to a stadium, then to a rounded rect.
final
BorderSide
lerpedSide
=
BorderSide
.
lerp
(
side
,
b
.
side
,
t
);
return
_twoPhaseLerp
(
t
,
2
/
3
,
(
double
t
)
{
return
_twoPhaseLerp
(
t
,
0.5
,
(
double
t
)
=>
lerpTo
(
CircleBorder
(
side:
lerpedSide
),
t
),
(
double
t
)
=>
StadiumBorder
(
side:
lerpedSide
).
lerpFrom
(
CircleBorder
(
side:
lerpedSide
),
t
),
);
},
(
double
t
)
{
return
StadiumBorder
(
side:
lerpedSide
).
lerpTo
(
b
,
t
);
},
);
}
return
super
.
lerpTo
(
b
,
t
);
}
@override
StarBorder
copyWith
({
BorderSide
?
side
,
double
?
points
,
double
?
innerRadiusRatio
,
double
?
pointRounding
,
double
?
valleyRounding
,
double
?
rotation
,
double
?
squash
,
})
{
return
StarBorder
(
side:
side
??
this
.
side
,
points:
points
??
this
.
points
,
rotation:
rotation
??
this
.
rotation
,
innerRadiusRatio:
innerRadiusRatio
??
this
.
innerRadiusRatio
,
pointRounding:
pointRounding
??
this
.
pointRounding
,
valleyRounding:
valleyRounding
??
this
.
valleyRounding
,
squash:
squash
??
this
.
squash
,
);
}
@override
Path
getInnerPath
(
Rect
rect
,
{
TextDirection
?
textDirection
})
{
final
Rect
adjustedRect
;
switch
(
side
.
strokeAlign
)
{
case
StrokeAlign
.
inside
:
adjustedRect
=
rect
.
deflate
(
side
.
width
);
break
;
case
StrokeAlign
.
center
:
adjustedRect
=
rect
.
deflate
(
side
.
width
/
2
);
break
;
case
StrokeAlign
.
outside
:
adjustedRect
=
rect
;
break
;
}
return
_StarGenerator
(
points:
points
,
rotation:
_rotationRadians
,
innerRadiusRatio:
innerRadiusRatio
,
pointRounding:
pointRounding
,
valleyRounding:
valleyRounding
,
squash:
squash
,
).
generate
(
adjustedRect
);
}
@override
Path
getOuterPath
(
Rect
rect
,
{
TextDirection
?
textDirection
})
{
return
_StarGenerator
(
points:
points
,
rotation:
_rotationRadians
,
innerRadiusRatio:
innerRadiusRatio
,
pointRounding:
pointRounding
,
valleyRounding:
valleyRounding
,
squash:
squash
,
).
generate
(
rect
);
}
@override
void
paint
(
Canvas
canvas
,
Rect
rect
,
{
TextDirection
?
textDirection
})
{
switch
(
side
.
style
)
{
case
BorderStyle
.
none
:
break
;
case
BorderStyle
.
solid
:
final
Rect
adjustedRect
;
switch
(
side
.
strokeAlign
)
{
case
StrokeAlign
.
inside
:
adjustedRect
=
rect
.
deflate
(
side
.
width
/
2
);
break
;
case
StrokeAlign
.
center
:
adjustedRect
=
rect
;
break
;
case
StrokeAlign
.
outside
:
adjustedRect
=
rect
.
inflate
(
side
.
width
/
2
);
break
;
}
final
Path
path
=
_StarGenerator
(
points:
points
,
rotation:
_rotationRadians
,
innerRadiusRatio:
innerRadiusRatio
,
pointRounding:
pointRounding
,
valleyRounding:
valleyRounding
,
squash:
squash
,
).
generate
(
adjustedRect
);
canvas
.
drawPath
(
path
,
side
.
toPaint
());
}
}
@override
bool
operator
==(
Object
other
)
{
if
(
other
.
runtimeType
!=
runtimeType
)
{
return
false
;
}
return
other
is
StarBorder
&&
other
.
side
==
side
;
}
@override
int
get
hashCode
=>
side
.
hashCode
;
@override
String
toString
()
{
return
'
${objectRuntimeType(this, 'StarBorder')}
(
$side
, points:
$points
, innerRadiusRatio:
$innerRadiusRatio
)'
;
}
}
class
_PointInfo
{
_PointInfo
({
required
this
.
valley
,
required
this
.
point
,
required
this
.
valleyArc1
,
required
this
.
pointArc1
,
required
this
.
valleyArc2
,
required
this
.
pointArc2
,
});
Offset
valley
;
Offset
point
;
Offset
valleyArc1
;
Offset
pointArc1
;
Offset
pointArc2
;
Offset
valleyArc2
;
}
class
_StarGenerator
{
const
_StarGenerator
({
required
this
.
points
,
required
this
.
innerRadiusRatio
,
required
this
.
pointRounding
,
required
this
.
valleyRounding
,
required
this
.
rotation
,
required
this
.
squash
,
})
:
assert
(
points
>
1
),
assert
(
innerRadiusRatio
==
null
||
innerRadiusRatio
<=
1
),
assert
(
innerRadiusRatio
==
null
||
innerRadiusRatio
>=
0
),
assert
(
squash
>=
0
),
assert
(
squash
<=
1
),
assert
(
pointRounding
>=
0
),
assert
(
pointRounding
<=
1
),
assert
(
valleyRounding
>=
0
),
assert
(
valleyRounding
<=
1
),
assert
(
pointRounding
+
valleyRounding
<=
1
);
final
double
points
;
final
double
innerRadiusRatio
;
final
double
pointRounding
;
final
double
valleyRounding
;
final
double
rotation
;
final
double
squash
;
bool
get
isStar
=>
innerRadiusRatio
!=
null
;
Path
generate
(
Rect
rect
)
{
final
double
radius
=
rect
.
shortestSide
/
2
;
final
Offset
center
=
rect
.
center
;
// The minimum allowed inner radius ratio. Numerical instabilities occur near
// zero, so we just don't allow values in that range.
const
double
minInnerRadiusRatio
=
.
002
;
// Map the innerRadiusRatio so that we don't get values close to zero, since
// things get a little squirrelly there because the path thinks that the
// length of the conicTo is small enough that it can render it as a straight
// line, even though it will be scaled up later. This maps the range from
// [0, 1] to [minInnerRadiusRatio, 1].
final
double
mappedInnerRadiusRatio
=
(
innerRadiusRatio
*
(
1.0
-
minInnerRadiusRatio
))
+
minInnerRadiusRatio
;
// First, generate the "points" of the star.
final
List
<
_PointInfo
>
points
=
<
_PointInfo
>[];
final
double
maxDiameter
=
2.0
*
_generatePoints
(
pointList:
points
,
center:
center
,
radius:
radius
,
innerRadius:
radius
*
mappedInnerRadiusRatio
,
);
// Calculate the endpoints of each of the arcs, then draw the arcs.
final
Path
path
=
Path
();
_drawPoints
(
path
,
points
);
Offset
scale
=
Offset
(
rect
.
width
/
maxDiameter
,
rect
.
height
/
maxDiameter
);
if
(
rect
.
shortestSide
==
rect
.
width
)
{
scale
=
Offset
(
scale
.
dx
,
squash
*
scale
.
dy
+
(
1
-
squash
)
*
scale
.
dx
);
}
else
{
scale
=
Offset
(
squash
*
scale
.
dx
+
(
1
-
squash
)
*
scale
.
dy
,
scale
.
dy
);
}
// Scale the border so that it matches the size of the widget rectangle, so
// that "rotation" of the shape doesn't affect how much of the rectangle it
// covers.
final
Matrix4
squashMatrix
=
Matrix4
.
translationValues
(
rect
.
center
.
dx
,
rect
.
center
.
dy
,
0
);
squashMatrix
.
multiply
(
Matrix4
.
diagonal3Values
(
scale
.
dx
,
scale
.
dy
,
1
));
squashMatrix
.
multiply
(
Matrix4
.
rotationZ
(
rotation
));
squashMatrix
.
multiply
(
Matrix4
.
translationValues
(-
rect
.
center
.
dx
,
-
rect
.
center
.
dy
,
0
));
return
path
.
transform
(
squashMatrix
.
storage
);
}
double
_generatePoints
({
required
List
<
_PointInfo
>
pointList
,
required
Offset
center
,
required
double
radius
,
required
double
innerRadius
,
})
{
final
double
step
=
math
.
pi
/
points
;
// Start initial rotation one step before zero.
double
angle
=
-
math
.
pi
/
2
-
step
;
Offset
valley
=
Offset
(
center
.
dx
+
math
.
cos
(
angle
)
*
innerRadius
,
center
.
dy
+
math
.
sin
(
angle
)
*
innerRadius
,
);
// In order to do overall scale properly, calculate the actual radius at the
// point, taking into account the rounding of the points and the weight of
// the corner point. This effectively is evaluating the rational quadratic
// bezier at the midpoint of the curve.
Offset
getCurveMidpoint
(
Offset
a
,
Offset
b
,
Offset
c
,
Offset
a1
,
Offset
c1
)
{
final
double
angle
=
_getAngle
(
a
,
b
,
c
);
final
double
w
=
_getWeight
(
angle
)
/
2
;
return
(
a1
/
4
+
b
*
w
+
c1
/
4
)
/
(
0.5
+
w
);
}
double
addPoint
(
double
pointAngle
,
double
pointStep
,
double
pointRadius
,
double
pointInnerRadius
,
)
{
pointAngle
+=
pointStep
;
final
Offset
point
=
Offset
(
center
.
dx
+
math
.
cos
(
pointAngle
)
*
pointRadius
,
center
.
dy
+
math
.
sin
(
pointAngle
)
*
pointRadius
,
);
pointAngle
+=
pointStep
;
final
Offset
nextValley
=
Offset
(
center
.
dx
+
math
.
cos
(
pointAngle
)
*
pointInnerRadius
,
center
.
dy
+
math
.
sin
(
pointAngle
)
*
pointInnerRadius
,
);
final
Offset
valleyArc1
=
valley
+
(
point
-
valley
)
*
valleyRounding
;
final
Offset
pointArc1
=
point
+
(
valley
-
point
)
*
pointRounding
;
final
Offset
pointArc2
=
point
+
(
nextValley
-
point
)
*
pointRounding
;
final
Offset
valleyArc2
=
nextValley
+
(
point
-
nextValley
)
*
valleyRounding
;
pointList
.
add
(
_PointInfo
(
valley:
valley
,
point:
point
,
valleyArc1:
valleyArc1
,
pointArc1:
pointArc1
,
pointArc2:
pointArc2
,
valleyArc2:
valleyArc2
,
));
valley
=
nextValley
;
return
pointAngle
;
}
final
double
remainder
=
points
-
points
.
truncateToDouble
();
final
bool
hasIntegerSides
=
remainder
<
1
e
-
6
;
final
double
wholeSides
=
points
-
(
hasIntegerSides
?
0
:
1
);
for
(
int
i
=
0
;
i
<
wholeSides
;
i
+=
1
)
{
angle
=
addPoint
(
angle
,
step
,
radius
,
innerRadius
);
}
double
valleyRadius
=
0
;
double
pointRadius
=
0
;
final
_PointInfo
thisPoint
=
pointList
[
0
];
final
_PointInfo
nextPoint
=
pointList
[
1
];
final
Offset
pointMidpoint
=
getCurveMidpoint
(
thisPoint
.
valley
,
thisPoint
.
point
,
nextPoint
.
valley
,
thisPoint
.
pointArc1
,
thisPoint
.
pointArc2
);
final
Offset
valleyMidpoint
=
getCurveMidpoint
(
thisPoint
.
point
,
nextPoint
.
valley
,
nextPoint
.
point
,
thisPoint
.
valleyArc2
,
nextPoint
.
valleyArc1
);
valleyRadius
=
(
valleyMidpoint
-
center
).
distance
;
pointRadius
=
(
pointMidpoint
-
center
).
distance
;
// Add the final point to close the shape if there are fractional sides to
// account for.
if
(!
hasIntegerSides
)
{
final
double
effectiveInnerRadius
=
math
.
max
(
valleyRadius
,
innerRadius
);
final
double
endingRadius
=
effectiveInnerRadius
+
remainder
*
(
radius
-
effectiveInnerRadius
);
addPoint
(
angle
,
step
*
remainder
,
endingRadius
,
innerRadius
);
}
// The rounding added to the valley radius can sometimes push it outside of
// the rounding of the point, since the rounding amount can be different, so
// we have to evaluate both the valley and the point radii, and pick the
// largest.
return
math
.
max
(
valleyRadius
,
pointRadius
);
}
void
_drawPoints
(
Path
path
,
List
<
_PointInfo
>
points
)
{
final
Offset
startingPoint
=
points
.
first
.
pointArc1
;
path
.
moveTo
(
startingPoint
.
dx
,
startingPoint
.
dy
);
final
double
pointAngle
=
_getAngle
(
points
[
0
].
valley
,
points
[
0
].
point
,
points
[
1
].
valley
);
final
double
pointWeight
=
_getWeight
(
pointAngle
);
final
double
valleyAngle
=
_getAngle
(
points
[
1
].
point
,
points
[
1
].
valley
,
points
[
0
].
point
);
final
double
valleyWeight
=
_getWeight
(
valleyAngle
);
for
(
int
i
=
0
;
i
<
points
.
length
;
i
+=
1
)
{
final
_PointInfo
point
=
points
[
i
];
final
_PointInfo
nextPoint
=
points
[(
i
+
1
)
%
points
.
length
];
path
.
lineTo
(
point
.
pointArc1
.
dx
,
point
.
pointArc1
.
dy
);
if
(
pointAngle
!=
180
&&
pointAngle
!=
0
)
{
path
.
conicTo
(
point
.
point
.
dx
,
point
.
point
.
dy
,
point
.
pointArc2
.
dx
,
point
.
pointArc2
.
dy
,
pointWeight
);
}
else
{
path
.
lineTo
(
point
.
pointArc2
.
dx
,
point
.
pointArc2
.
dy
);
}
path
.
lineTo
(
point
.
valleyArc2
.
dx
,
point
.
valleyArc2
.
dy
);
if
(
valleyAngle
!=
180
&&
valleyAngle
!=
0
)
{
path
.
conicTo
(
nextPoint
.
valley
.
dx
,
nextPoint
.
valley
.
dy
,
nextPoint
.
valleyArc1
.
dx
,
nextPoint
.
valleyArc1
.
dy
,
valleyWeight
);
}
else
{
path
.
lineTo
(
nextPoint
.
valleyArc1
.
dx
,
nextPoint
.
valleyArc1
.
dy
);
}
}
path
.
close
();
}
double
_getWeight
(
double
angle
)
{
return
math
.
cos
((
angle
/
2
)
%
(
math
.
pi
/
2
));
}
// Returns the included angle between points ABC in radians.
double
_getAngle
(
Offset
a
,
Offset
b
,
Offset
c
)
{
if
(
a
==
c
||
b
==
c
||
b
==
a
)
{
return
0
;
}
final
Offset
u
=
a
-
b
;
final
Offset
v
=
c
-
b
;
final
double
dot
=
u
.
dx
*
v
.
dx
+
u
.
dy
*
v
.
dy
;
final
double
m1
=
b
.
dx
==
a
.
dx
?
double
.
infinity
:
-
u
.
dy
/
-
u
.
dx
;
final
double
m2
=
b
.
dx
==
c
.
dx
?
double
.
infinity
:
-
v
.
dy
/
-
v
.
dx
;
double
angle
=
math
.
atan2
(
m1
-
m2
,
1
+
m1
*
m2
).
abs
();
if
(
dot
<
0
)
{
angle
+=
math
.
pi
;
}
return
angle
;
}
}
packages/flutter/test/painting/star_border_test.dart
0 → 100644
View file @
07de84f0
// 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.
@Tags
(<
String
>[
'reduced-test-set'
])
import
'package:flutter/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
Future
<
void
>
testBorder
(
WidgetTester
tester
,
String
name
,
StarBorder
border
,
{
ShapeBorder
?
lerpTo
,
ShapeBorder
?
lerpFrom
,
double
lerpAmount
=
0
})
async
{
assert
(
lerpTo
==
null
||
lerpFrom
==
null
);
// They can't both be set.
ShapeBorder
shape
;
if
(
lerpTo
!=
null
)
{
shape
=
border
.
lerpTo
(
lerpTo
,
lerpAmount
)!;
}
else
if
(
lerpFrom
!=
null
)
{
shape
=
border
.
lerpFrom
(
lerpFrom
,
lerpAmount
)!;
}
else
{
shape
=
border
;
}
await
tester
.
pumpWidget
(
Container
(
alignment:
Alignment
.
center
,
width:
200
,
height:
100
,
decoration:
ShapeDecoration
(
color:
const
Color
(
0xff000000
),
shape:
shape
,
),
),
);
await
expectLater
(
find
.
byType
(
Container
),
matchesGoldenFile
(
'painting.star_border.
$name
.png'
),
);
}
test
(
'StarBorder defaults'
,
()
{
const
StarBorder
star
=
StarBorder
();
expect
(
star
.
side
,
BorderSide
.
none
);
expect
(
star
.
points
,
5
);
expect
(
star
.
innerRadiusRatio
,
0.4
);
expect
(
star
.
rotation
,
0
);
expect
(
star
.
pointRounding
,
0
);
expect
(
star
.
valleyRounding
,
0
);
expect
(
star
.
squash
,
0
);
const
StarBorder
polygon
=
StarBorder
.
polygon
();
expect
(
polygon
.
points
,
5
);
expect
(
polygon
.
pointRounding
,
0
);
expect
(
polygon
.
rotation
,
0
);
expect
(
polygon
.
squash
,
0
);
});
test
(
'StarBorder copyWith, ==, hashCode'
,
()
{
const
BorderSide
side
=
BorderSide
(
width:
10.0
,
color:
Color
(
0xff123456
));
final
StarBorder
copy
=
const
StarBorder
().
copyWith
(
side:
side
,
points:
3
,
innerRadiusRatio:
0.1
,
pointRounding:
0.2
,
valleyRounding:
0.3
,
rotation:
180
,
squash:
0.4
,
);
const
StarBorder
expected
=
StarBorder
(
side:
side
,
points:
3
,
innerRadiusRatio:
0.1
,
pointRounding:
0.2
,
valleyRounding:
0.3
,
rotation:
180
,
squash:
0.4
,
);
expect
(
const
StarBorder
(),
equals
(
const
StarBorder
().
copyWith
()));
expect
(
copy
,
equals
(
expected
));
expect
(
copy
.
hashCode
,
equals
(
expected
.
hashCode
));
});
testWidgets
(
'StarBorder basic geometry'
,
(
WidgetTester
tester
)
async
{
await
testBorder
(
tester
,
'basic_star'
,
const
StarBorder
());
await
testBorder
(
tester
,
'basic_polygon'
,
const
StarBorder
.
polygon
());
});
testWidgets
(
'StarBorder parameters'
,
(
WidgetTester
tester
)
async
{
await
testBorder
(
tester
,
'points_6'
,
const
StarBorder
(
points:
6
));
await
testBorder
(
tester
,
'points_2'
,
const
StarBorder
(
points:
2
));
await
testBorder
(
tester
,
'inner_radius_0'
,
const
StarBorder
(
innerRadiusRatio:
0.0
));
await
testBorder
(
tester
,
'inner_radius_2'
,
const
StarBorder
(
innerRadiusRatio:
0.2
));
await
testBorder
(
tester
,
'inner_radius_7'
,
const
StarBorder
(
innerRadiusRatio:
0.7
));
await
testBorder
(
tester
,
'point_rounding_2'
,
const
StarBorder
(
pointRounding:
0.2
));
await
testBorder
(
tester
,
'point_rounding_7'
,
const
StarBorder
(
pointRounding:
0.7
));
await
testBorder
(
tester
,
'point_rounding_10'
,
const
StarBorder
(
pointRounding:
1.0
));
await
testBorder
(
tester
,
'valley_rounding_2'
,
const
StarBorder
(
valleyRounding:
0.2
));
await
testBorder
(
tester
,
'valley_rounding_7'
,
const
StarBorder
(
valleyRounding:
0.7
));
await
testBorder
(
tester
,
'valley_rounding_10'
,
const
StarBorder
(
valleyRounding:
1.0
));
await
testBorder
(
tester
,
'squash_2'
,
const
StarBorder
(
squash:
0.2
));
await
testBorder
(
tester
,
'squash_7'
,
const
StarBorder
(
squash:
0.7
));
await
testBorder
(
tester
,
'squash_10'
,
const
StarBorder
(
squash:
1.0
));
await
testBorder
(
tester
,
'rotate_27'
,
const
StarBorder
(
rotation:
27
));
await
testBorder
(
tester
,
'rotate_270'
,
const
StarBorder
(
rotation:
270
));
await
testBorder
(
tester
,
'rotate_360'
,
const
StarBorder
(
rotation:
360
));
await
testBorder
(
tester
,
'side_none'
,
const
StarBorder
(
side:
BorderSide
(
style:
BorderStyle
.
none
)));
await
testBorder
(
tester
,
'side_1'
,
const
StarBorder
(
side:
BorderSide
(
color:
Color
(
0xffff0000
))));
await
testBorder
(
tester
,
'side_10'
,
const
StarBorder
(
side:
BorderSide
(
color:
Color
(
0xffff0000
),
width:
10
)));
await
testBorder
(
tester
,
'side_align_center'
,
const
StarBorder
(
side:
BorderSide
(
color:
Color
(
0xffff0000
),
strokeAlign:
StrokeAlign
.
center
)));
await
testBorder
(
tester
,
'side_align_outside'
,
const
StarBorder
(
side:
BorderSide
(
color:
Color
(
0xffff0000
),
strokeAlign:
StrokeAlign
.
outside
)));
});
testWidgets
(
'StarBorder.polygon parameters'
,
(
WidgetTester
tester
)
async
{
await
testBorder
(
tester
,
'poly_sides_6'
,
const
StarBorder
.
polygon
(
sides:
6
));
await
testBorder
(
tester
,
'poly_sides_2'
,
const
StarBorder
.
polygon
(
sides:
2
));
await
testBorder
(
tester
,
'poly_point_rounding_2'
,
const
StarBorder
.
polygon
(
pointRounding:
0.2
));
await
testBorder
(
tester
,
'poly_point_rounding_7'
,
const
StarBorder
.
polygon
(
pointRounding:
0.7
));
await
testBorder
(
tester
,
'poly_point_rounding_10'
,
const
StarBorder
.
polygon
(
pointRounding:
1.0
));
await
testBorder
(
tester
,
'poly_squash_2'
,
const
StarBorder
.
polygon
(
squash:
0.2
));
await
testBorder
(
tester
,
'poly_squash_7'
,
const
StarBorder
.
polygon
(
squash:
0.7
));
await
testBorder
(
tester
,
'poly_squash_10'
,
const
StarBorder
.
polygon
(
squash:
1.0
));
await
testBorder
(
tester
,
'poly_rotate_27'
,
const
StarBorder
.
polygon
(
rotation:
27
));
await
testBorder
(
tester
,
'poly_rotate_270'
,
const
StarBorder
.
polygon
(
rotation:
270
));
await
testBorder
(
tester
,
'poly_rotate_360'
,
const
StarBorder
.
polygon
(
rotation:
360
));
await
testBorder
(
tester
,
'poly_side_none'
,
const
StarBorder
.
polygon
(
side:
BorderSide
(
style:
BorderStyle
.
none
)));
await
testBorder
(
tester
,
'poly_side_1'
,
const
StarBorder
.
polygon
(
side:
BorderSide
(
color:
Color
(
0xffff0000
))));
await
testBorder
(
tester
,
'poly_side_10'
,
const
StarBorder
.
polygon
(
side:
BorderSide
(
color:
Color
(
0xffff0000
),
width:
10
)));
await
testBorder
(
tester
,
'poly_side_align_center'
,
const
StarBorder
.
polygon
(
side:
BorderSide
(
color:
Color
(
0xffff0000
),
strokeAlign:
StrokeAlign
.
center
)));
await
testBorder
(
tester
,
'poly_side_align_outside'
,
const
StarBorder
.
polygon
(
side:
BorderSide
(
color:
Color
(
0xffff0000
),
strokeAlign:
StrokeAlign
.
outside
)));
});
testWidgets
(
'StarBorder lerped with StarBorder'
,
(
WidgetTester
tester
)
async
{
const
StarBorder
from
=
StarBorder
();
const
ShapeBorder
otherBorder
=
StarBorder
(
points:
6
,
pointRounding:
0.5
,
valleyRounding:
0.5
,
innerRadiusRatio:
0.5
,
rotation:
90
,
);
await
testBorder
(
tester
,
'to_star_border_2'
,
from
,
lerpTo:
otherBorder
,
lerpAmount:
0.2
);
await
testBorder
(
tester
,
'to_star_border_7'
,
from
,
lerpTo:
otherBorder
,
lerpAmount:
0.7
);
await
testBorder
(
tester
,
'to_star_border_10'
,
from
,
lerpTo:
otherBorder
,
lerpAmount:
1.0
);
await
testBorder
(
tester
,
'from_star_border_2'
,
from
,
lerpFrom:
otherBorder
,
lerpAmount:
0.2
);
await
testBorder
(
tester
,
'from_star_border_7'
,
from
,
lerpFrom:
otherBorder
,
lerpAmount:
0.7
);
await
testBorder
(
tester
,
'from_star_border_10'
,
from
,
lerpFrom:
otherBorder
,
lerpAmount:
1.0
);
});
testWidgets
(
'StarBorder lerped with CircleBorder'
,
(
WidgetTester
tester
)
async
{
const
StarBorder
from
=
StarBorder
();
const
ShapeBorder
otherBorder
=
CircleBorder
();
await
testBorder
(
tester
,
'to_circle_border_2'
,
from
,
lerpTo:
otherBorder
,
lerpAmount:
0.2
);
await
testBorder
(
tester
,
'to_circle_border_7'
,
from
,
lerpTo:
otherBorder
,
lerpAmount:
0.7
);
await
testBorder
(
tester
,
'to_circle_border_10'
,
from
,
lerpTo:
otherBorder
,
lerpAmount:
1.0
);
await
testBorder
(
tester
,
'from_circle_border_2'
,
from
,
lerpFrom:
otherBorder
,
lerpAmount:
0.2
);
await
testBorder
(
tester
,
'from_circle_border_7'
,
from
,
lerpFrom:
otherBorder
,
lerpAmount:
0.7
);
await
testBorder
(
tester
,
'from_circle_border_10'
,
from
,
lerpFrom:
otherBorder
,
lerpAmount:
1.0
);
});
testWidgets
(
'StarBorder lerped with RoundedRectangleBorder'
,
(
WidgetTester
tester
)
async
{
const
StarBorder
from
=
StarBorder
();
const
RoundedRectangleBorder
rectangleBorder
=
RoundedRectangleBorder
();
await
testBorder
(
tester
,
'to_rect_border_2'
,
from
,
lerpTo:
rectangleBorder
,
lerpAmount:
0.2
);
await
testBorder
(
tester
,
'to_rect_border_7'
,
from
,
lerpTo:
rectangleBorder
,
lerpAmount:
0.7
);
await
testBorder
(
tester
,
'to_rect_border_10'
,
from
,
lerpTo:
rectangleBorder
,
lerpAmount:
1.0
);
await
testBorder
(
tester
,
'from_rect_border_2'
,
from
,
lerpFrom:
rectangleBorder
,
lerpAmount:
0.2
);
await
testBorder
(
tester
,
'from_rect_border_7'
,
from
,
lerpFrom:
rectangleBorder
,
lerpAmount:
0.7
);
await
testBorder
(
tester
,
'from_rect_border_10'
,
from
,
lerpFrom:
rectangleBorder
,
lerpAmount:
1.0
);
const
RoundedRectangleBorder
roundedRectBorder
=
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
only
(
bottomLeft:
Radius
.
circular
(
10.0
),
bottomRight:
Radius
.
circular
(
10.0
),
),
);
await
testBorder
(
tester
,
'to_rrect_border_2'
,
from
,
lerpTo:
roundedRectBorder
,
lerpAmount:
0.2
);
await
testBorder
(
tester
,
'to_rrect_border_7'
,
from
,
lerpTo:
roundedRectBorder
,
lerpAmount:
0.7
);
await
testBorder
(
tester
,
'to_rrect_border_10'
,
from
,
lerpTo:
roundedRectBorder
,
lerpAmount:
1.0
);
await
testBorder
(
tester
,
'from_rrect_border_2'
,
from
,
lerpFrom:
roundedRectBorder
,
lerpAmount:
0.2
);
await
testBorder
(
tester
,
'from_rrect_border_7'
,
from
,
lerpFrom:
roundedRectBorder
,
lerpAmount:
0.7
);
await
testBorder
(
tester
,
'from_rrect_border_10'
,
from
,
lerpFrom:
roundedRectBorder
,
lerpAmount:
1.0
);
});
testWidgets
(
'StarBorder lerped with StadiumBorder'
,
(
WidgetTester
tester
)
async
{
const
StarBorder
from
=
StarBorder
();
const
StadiumBorder
stadiumBorder
=
StadiumBorder
();
await
testBorder
(
tester
,
'to_stadium_border_2'
,
from
,
lerpTo:
stadiumBorder
,
lerpAmount:
0.2
);
await
testBorder
(
tester
,
'to_stadium_border_7'
,
from
,
lerpTo:
stadiumBorder
,
lerpAmount:
0.7
);
await
testBorder
(
tester
,
'to_stadium_border_10'
,
from
,
lerpTo:
stadiumBorder
,
lerpAmount:
1.0
);
await
testBorder
(
tester
,
'from_stadium_border_2'
,
from
,
lerpFrom:
stadiumBorder
,
lerpAmount:
0.2
);
await
testBorder
(
tester
,
'from_stadium_border_7'
,
from
,
lerpFrom:
stadiumBorder
,
lerpAmount:
0.7
);
await
testBorder
(
tester
,
'from_stadium_border_10'
,
from
,
lerpFrom:
stadiumBorder
,
lerpAmount:
1.0
);
});
}
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