Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 3a181e4

Browse files
authored
Added LinearBorder, an OutlinedBorder like BoxBorder (#116940)
1 parent fb1a151 commit 3a181e4

File tree

5 files changed

+901
-0
lines changed

5 files changed

+901
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Examples of LinearBorder and LinearBorderEdge.
6+
7+
8+
import 'package:flutter/material.dart';
9+
10+
void main() {
11+
runApp(const ExampleApp());
12+
}
13+
14+
class ExampleApp extends StatelessWidget {
15+
const ExampleApp({ super.key });
16+
17+
@override
18+
Widget build(BuildContext context) {
19+
return MaterialApp(
20+
theme: ThemeData.light(useMaterial3: true),
21+
home: const Directionality(
22+
textDirection: TextDirection.ltr, // Or try rtl.
23+
child: Home(),
24+
),
25+
);
26+
}
27+
}
28+
29+
class SampleCard extends StatelessWidget {
30+
const SampleCard({ super.key, required this.title, required this.subtitle, required this.children });
31+
32+
final String title;
33+
final String subtitle;
34+
final List<Widget> children;
35+
36+
@override
37+
Widget build(BuildContext context) {
38+
final ThemeData theme = Theme.of(context);
39+
final TextTheme textTheme = theme.textTheme;
40+
final ColorScheme colorScheme = theme.colorScheme;
41+
42+
return Card(
43+
child: Padding(
44+
padding: const EdgeInsets.all(16),
45+
child: Column(
46+
crossAxisAlignment: CrossAxisAlignment.start,
47+
mainAxisSize: MainAxisSize.min,
48+
children: <Widget>[
49+
Text(title, style: textTheme.titleMedium),
50+
Text(subtitle, style: textTheme.bodyMedium!.copyWith(color: colorScheme.secondary)),
51+
const SizedBox(height: 16),
52+
Row(
53+
children: List<Widget>.generate(children.length * 2 - 1, (int index) {
54+
return index.isEven ? children[index ~/2] : const SizedBox(width: 16);
55+
}),
56+
),
57+
],
58+
),
59+
),
60+
);
61+
}
62+
}
63+
64+
class Home extends StatefulWidget {
65+
const Home({ super.key });
66+
67+
@override
68+
State<Home> createState() => _HomeState();
69+
}
70+
71+
class _HomeState extends State<Home> {
72+
final LinearBorder shape0 = LinearBorder.top();
73+
final LinearBorder shape1 = LinearBorder.top(size: 0);
74+
late LinearBorder shape = shape0;
75+
76+
@override
77+
Widget build(BuildContext context) {
78+
final ColorScheme colorScheme = Theme.of(context).colorScheme;
79+
final BorderSide primarySide0 = BorderSide(width: 0, color: colorScheme.inversePrimary); // hairline
80+
final BorderSide primarySide2 = BorderSide(width: 2, color: colorScheme.onPrimaryContainer);
81+
final BorderSide primarySide3 = BorderSide(width: 3, color: colorScheme.inversePrimary);
82+
83+
return Scaffold(
84+
body: SingleChildScrollView(
85+
child: Padding(
86+
padding: const EdgeInsets.all(16),
87+
child: Column(
88+
mainAxisSize: MainAxisSize.min,
89+
children: <Widget>[
90+
// Demonstrates using LinearBorder.bottom() to define
91+
// an underline border for the standard button types.
92+
// The underline's color and width is defined by the ButtonStyle's
93+
// side parameter. The side can also be specified as a
94+
// LinearBorder parameter and if both are specified then the
95+
// ButtonStyle's side is used. This set up makes it possible
96+
// for a button theme to specify the shape and for indidividual
97+
// buttons to specify the shape border's color and width.
98+
SampleCard(
99+
title: 'LinearBorder.bottom()',
100+
subtitle: 'Standard button widgets',
101+
children: <Widget>[
102+
TextButton(
103+
style: TextButton.styleFrom(
104+
side: primarySide3,
105+
shape: LinearBorder.bottom(),
106+
),
107+
onPressed: () { },
108+
child: const Text('Text'),
109+
),
110+
OutlinedButton(
111+
style: OutlinedButton.styleFrom(
112+
side: primarySide3,
113+
shape: LinearBorder.bottom(),
114+
),
115+
onPressed: () { },
116+
child: const Text('Outlined'),
117+
),
118+
ElevatedButton(
119+
style: ElevatedButton.styleFrom(
120+
side: primarySide3,
121+
shape: LinearBorder.bottom(),
122+
),
123+
onPressed: () { },
124+
child: const Text('Elevated'),
125+
),
126+
],
127+
),
128+
const SizedBox(height: 32),
129+
// Demonstrates creating LinearBorders with a single edge
130+
// by using the convenience constructors like LinearBorder.start().
131+
// The edges are drawn with a BorderSide with width:0, which
132+
// means that a "hairline" line is stroked. Wider borders are
133+
// drawn with filled rectangles.
134+
SampleCard(
135+
title: 'LinearBorder',
136+
subtitle: 'Convenience constructors',
137+
children: <Widget>[
138+
TextButton(
139+
style: TextButton.styleFrom(
140+
side: primarySide0,
141+
shape: LinearBorder.start(),
142+
),
143+
onPressed: () { },
144+
child: const Text('Start()'),
145+
),
146+
TextButton(
147+
style: TextButton.styleFrom(
148+
side: primarySide0,
149+
shape: LinearBorder.end(),
150+
),
151+
onPressed: () { },
152+
child: const Text('End()'),
153+
),
154+
TextButton(
155+
style: TextButton.styleFrom(
156+
side: primarySide0,
157+
shape: LinearBorder.top(),
158+
),
159+
onPressed: () { },
160+
child: const Text('Top()'),
161+
),
162+
TextButton(
163+
style: TextButton.styleFrom(
164+
side: primarySide0,
165+
shape: LinearBorder.bottom(),
166+
),
167+
onPressed: () { },
168+
child: const Text('Bottom()'),
169+
),
170+
],
171+
),
172+
const SizedBox(height: 32),
173+
// Demonstrates creating LinearBorders with a single edge
174+
// that's smaller than the button's bounding box. The size
175+
// parameter specifies a percentage of the available space
176+
// and alignment is -1 for start-alignment, 0 for centered,
177+
// and 1 for end-alignment.
178+
SampleCard(
179+
title: 'LinearBorder',
180+
subtitle: 'Size and alignment parameters',
181+
children: <Widget>[
182+
TextButton(
183+
style: TextButton.styleFrom(
184+
side: primarySide2,
185+
shape: LinearBorder.bottom(
186+
size: 0.5,
187+
),
188+
),
189+
onPressed: () { },
190+
child: const Text('Center'),
191+
),
192+
TextButton(
193+
style: TextButton.styleFrom(
194+
side: primarySide2,
195+
shape: LinearBorder.bottom(
196+
size: 0.75,
197+
alignment: -1,
198+
),
199+
),
200+
onPressed: () { },
201+
child: const Text('Start'),
202+
),
203+
TextButton(
204+
style: TextButton.styleFrom(
205+
side: primarySide2,
206+
shape: LinearBorder.bottom(
207+
size: 0.75,
208+
alignment: 1,
209+
),
210+
),
211+
onPressed: () { },
212+
child: const Text('End'),
213+
),
214+
],
215+
),
216+
const SizedBox(height: 32),
217+
// Demonstrates creating LinearBorders with more than one edge.
218+
// In these cases the default constructor is used and each edge
219+
// is defined with one LinearBorderEdge object.
220+
SampleCard(
221+
title: 'LinearBorder',
222+
subtitle: 'LinearBorderEdge parameters',
223+
children: <Widget>[
224+
TextButton(
225+
style: TextButton.styleFrom(
226+
side: primarySide0,
227+
shape: const LinearBorder(
228+
top: LinearBorderEdge(),
229+
bottom: LinearBorderEdge(),
230+
),
231+
),
232+
onPressed: () { },
233+
child: const Text('Horizontal'),
234+
),
235+
TextButton(
236+
style: TextButton.styleFrom(
237+
side: primarySide0,
238+
shape: const LinearBorder(
239+
start: LinearBorderEdge(),
240+
end: LinearBorderEdge(),
241+
),
242+
),
243+
onPressed: () { },
244+
child: const Text('Vertical'),
245+
),
246+
TextButton(
247+
style: TextButton.styleFrom(
248+
side: primarySide0,
249+
shape: const LinearBorder(
250+
start: LinearBorderEdge(),
251+
bottom: LinearBorderEdge(),
252+
),
253+
),
254+
onPressed: () { },
255+
child: const Text('Corner'),
256+
),
257+
],
258+
),
259+
const SizedBox(height: 32),
260+
// Demonstrates that changing properties of LinearBorders
261+
// causes them to animate to their new configuration.
262+
SampleCard(
263+
title: 'Interpolation',
264+
subtitle: 'LinearBorder.top() => LinearBorder.top(size: 0)',
265+
children: <Widget>[
266+
IconButton(
267+
icon: const Icon(Icons.play_arrow),
268+
onPressed: () {
269+
setState(() {
270+
shape = shape == shape0 ? shape1 : shape0;
271+
});
272+
},
273+
),
274+
TextButton(
275+
style: TextButton.styleFrom(
276+
side: primarySide3,
277+
shape: shape,
278+
),
279+
onPressed: () { },
280+
child: const Text('Press Play'),
281+
),
282+
TextButton(
283+
style: ButtonStyle(
284+
side: MaterialStateProperty.resolveWith<BorderSide?>((Set <MaterialState> states) {
285+
return states.contains(MaterialState.hovered) ? primarySide3 : null;
286+
}),
287+
shape: MaterialStateProperty.resolveWith<OutlinedBorder>((Set <MaterialState> states) {
288+
return states.contains(MaterialState.hovered) ? shape0 : shape1;
289+
}),
290+
291+
),
292+
onPressed: () { },
293+
child: const Text('Hover'),
294+
),
295+
],
296+
),
297+
],
298+
),
299+
),
300+
),
301+
);
302+
}
303+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
7+
import 'package:flutter_api_samples/painting/linear_border/linear_border.0.dart' as example;
8+
import 'package:flutter_test/flutter_test.dart';
9+
10+
void main() {
11+
testWidgets('Smoke Test', (WidgetTester tester) async {
12+
await tester.pumpWidget(
13+
const example.ExampleApp(),
14+
);
15+
expect(find.byType(example.Home), findsOneWidget);
16+
17+
// Scroll the interpolation example into view
18+
19+
await tester.scrollUntilVisible(
20+
find.byIcon(Icons.play_arrow),
21+
500.0,
22+
scrollable: find.byType(Scrollable),
23+
);
24+
expect(find.byIcon(Icons.play_arrow), findsOneWidget);
25+
26+
// Run the interpolation example
27+
28+
await tester.tap(find.byIcon(Icons.play_arrow));
29+
await tester.pumpAndSettle();
30+
31+
await tester.tap(find.byIcon(Icons.play_arrow));
32+
await tester.pumpAndSettle();
33+
34+
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.text('Interpolation')));
35+
await gesture.moveTo(tester.getCenter(find.text('Hover')));
36+
await tester.pumpAndSettle();
37+
await gesture.moveTo(tester.getCenter(find.text('Interpolation')));
38+
await tester.pumpAndSettle();
39+
});
40+
}

packages/flutter/lib/painting.dart

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export 'src/painting/image_provider.dart';
4747
export 'src/painting/image_resolution.dart';
4848
export 'src/painting/image_stream.dart';
4949
export 'src/painting/inline_span.dart';
50+
export 'src/painting/linear_border.dart';
5051
export 'src/painting/matrix_utils.dart';
5152
export 'src/painting/notched_shapes.dart';
5253
export 'src/painting/oval_border.dart';

0 commit comments

Comments
 (0)