Layout

Resizable

A box which children can be resized along either the horizontal or vertical axis.

1class ResizableExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => DecoratedBox(
4 decoration: BoxDecoration(
5 border: .all(color: context.theme.colors.border),
6 borderRadius: .circular(8),
7 ),
8 child: FResizable(
9 axis: .vertical,
10 crossAxisExtent: 300,
11 children: [
12 .region(
13 initialExtent: 250,
14 minExtent: 100,
15 builder: (_, data, _) =>
16 Label(data: data, icon: FIcons.sunrise, label: 'Morning'),
17 ),
18 .region(
19 initialExtent: 100,
20 minExtent: 100,
21 builder: (_, data, _) =>
22 Label(data: data, icon: FIcons.sun, label: 'Afternoon'),
23 ),
24 .region(
25 initialExtent: 250,
26 minExtent: 100,
27 builder: (_, data, _) =>
28 Label(data: data, icon: FIcons.sunset, label: 'Evening'),
29 ),
30 ],
31 ),
32 );
33}
34
35class Label extends StatelessWidget {
36 static final format = DateFormat.jm();
37 final FResizableRegionData data;
38 final IconData icon;
39 final String label;
40 const Label({
41 required this.data,
42 required this.icon,
43 required this.label,
44 super.key,
45 });
46 @override
47 Widget build(BuildContext context) {
48 final FThemeData(:colors, :typography) = context.theme;
49 final start = DateTime.fromMillisecondsSinceEpoch(
50 (data.offsetPercentage.min * Duration.millisecondsPerDay).round(),
51 isUtc: true,
52 );
53 final end = DateTime.fromMillisecondsSinceEpoch(
54 (data.offsetPercentage.max * Duration.millisecondsPerDay).round(),
55 isUtc: true,
56 );
57 return Align(
58 child: Column(
59 mainAxisAlignment: .center,
60 children: [
61 Row(
62 mainAxisSize: .min,
63 mainAxisAlignment: .center,
64 children: [
65 Icon(icon, size: 15, color: colors.foreground),
66 const SizedBox(width: 3),
67 Text(
68 label,
69 style: typography.sm.copyWith(color: colors.foreground),
70 ),
71 ],
72 ),
73 const SizedBox(height: 5),
74 Text(
75 '${format.format(start)} - ${format.format(end)}',
76 style: typography.sm.copyWith(color: colors.foreground),
77 ),
78 ],
79 ),
80 );
81 }
82}
83

CLI

To generate and customize this style:

dart run forui style create resizable

Usage

FResizable(...)

1FResizable(
2 style: const .inherit(),
3 axis: .vertical,
4 divider: .dividerWithThumb,
5 children: [
6 FResizableRegion(
7 initialExtent: 200,
8 builder: (context, data, child) => child!,
9 ),
10 FResizableRegion(
11 initialExtent: 200,
12 minExtent: 100,
13 builder: (context, data, child) => child!,
14 ),
15 ],
16)

FResizableRegion(...)

1FResizableRegion(
2 initialExtent: 200,
3 minExtent: 100,
4 builder: (context, data, child) => child!,
5 child: const Placeholder(),
6)

Examples

Without Cascading

1class NoCascadingResizableExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => DecoratedBox(
4 decoration: BoxDecoration(
5 border: .all(color: context.theme.colors.border),
6 borderRadius: .circular(8),
7 ),
8 child: FResizable(
9 control: const .managed(),
10 axis: .vertical,
11 crossAxisExtent: 300,
12 children: [
13 .region(
14 initialExtent: 200,
15 minExtent: 100,
16 builder: (_, data, _) =>
17 Label(data: data, icon: FIcons.sunrise, label: 'Morning'),
18 ),
19 .region(
20 initialExtent: 200,
21 minExtent: 100,
22 builder: (_, data, _) =>
23 Label(data: data, icon: FIcons.sun, label: 'Afternoon'),
24 ),
25 .region(
26 initialExtent: 200,
27 minExtent: 100,
28 builder: (_, data, _) =>
29 Label(data: data, icon: FIcons.sunset, label: 'Evening'),
30 ),
31 ],
32 ),
33 );
34}
35
36class Label extends StatelessWidget {
37 static final format = DateFormat.jm();
38 final FResizableRegionData data;
39 final IconData icon;
40 final String label;
41 const Label({
42 required this.data,
43 required this.icon,
44 required this.label,
45 super.key,
46 });
47 @override
48 Widget build(BuildContext context) {
49 final FThemeData(:colors, :typography) = context.theme;
50 final start = DateTime.fromMillisecondsSinceEpoch(
51 (data.offsetPercentage.min * Duration.millisecondsPerDay).round(),
52 isUtc: true,
53 );
54 final end = DateTime.fromMillisecondsSinceEpoch(
55 (data.offsetPercentage.max * Duration.millisecondsPerDay).round(),
56 isUtc: true,
57 );
58 return Align(
59 child: Column(
60 mainAxisAlignment: .center,
61 children: [
62 Row(
63 mainAxisSize: .min,
64 mainAxisAlignment: .center,
65 children: [
66 Icon(icon, size: 15, color: colors.foreground),
67 const SizedBox(width: 3),
68 Text(
69 label,
70 style: typography.sm.copyWith(color: colors.foreground),
71 ),
72 ],
73 ),
74 const SizedBox(height: 5),
75 Text(
76 '${format.format(start)} - ${format.format(end)}',
77 style: typography.sm.copyWith(color: colors.foreground),
78 ),
79 ],
80 ),
81 );
82 }
83}
84

Horizontal

1@override
2Widget build(BuildContext context) => DecoratedBox(
3 decoration: BoxDecoration(
4 border: .all(color: context.theme.colors.border),
5 borderRadius: .circular(8),
6 ),
7 child: FResizable(
8 axis: .horizontal,
9 crossAxisExtent: 300,
10 children: [
11 .region(
12 initialExtent: 100,
13 minExtent: 100,
14 builder: (context, data, _) =>
15 Align(child: Text('Sidebar', style: context.theme.typography.sm)),
16 ),
17 .region(
18 initialExtent: 300,
19 minExtent: 100,
20 builder: (context, data, _) =>
21 Align(child: Text('Content', style: context.theme.typography.sm)),
22 ),
23 ],
24 ),
25);
26

Divider with No Thumb

1@override
2Widget build(BuildContext context) => DecoratedBox(
3 decoration: BoxDecoration(
4 border: .all(color: context.theme.colors.border),
5 borderRadius: .circular(8),
6 ),
7 child: FResizable(
8 axis: .horizontal,
9 divider: .divider,
10 crossAxisExtent: 300,
11 children: [
12 .region(
13 initialExtent: 100,
14 minExtent: 100,
15 builder: (context, data, _) =>
16 Align(child: Text('Sidebar', style: context.theme.typography.sm)),
17 ),
18 .region(
19 initialExtent: 300,
20 minExtent: 100,
21 builder: (context, data, _) =>
22 Align(child: Text('Content', style: context.theme.typography.sm)),
23 ),
24 ],
25 ),
26);
27

Without Divider

1@override
2Widget build(BuildContext context) => DecoratedBox(
3 decoration: BoxDecoration(
4 border: .all(color: context.theme.colors.border),
5 borderRadius: .circular(8),
6 ),
7 child: FResizable(
8 axis: .horizontal,
9 divider: .none,
10 crossAxisExtent: 300,
11 children: [
12 .region(
13 initialExtent: 100,
14 minExtent: 100,
15 builder: (context, data, _) =>
16 Align(child: Text('Sidebar', style: context.theme.typography.sm)),
17 ),
18 .region(
19 initialExtent: 300,
20 minExtent: 100,
21 builder: (context, data, _) =>
22 Align(child: Text('Content', style: context.theme.typography.sm)),
23 ),
24 ],
25 ),
26);
27

On this page