-
Notifications
You must be signed in to change notification settings - Fork 22
/
sheet_physics.dart
178 lines (159 loc) · 5.38 KB
/
sheet_physics.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import 'package:flutter/material.dart';
import 'package:smooth_sheets/smooth_sheets.dart';
void main() {
runApp(const _SheetPhysicsExample());
}
class _SheetPhysicsExample extends StatelessWidget {
const _SheetPhysicsExample();
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: _ExampleHome(),
);
}
}
enum _PhysicsKind {
clamping('Clamping'),
bouncing('Bouncing'),
clampingSnapping('Clamping + Snapping'),
bouncingSnapping('Bouncing + Snapping');
final String name;
const _PhysicsKind(this.name);
}
class _ExampleHome extends StatefulWidget {
const _ExampleHome();
@override
State<_ExampleHome> createState() => _ExampleHomeState();
}
class _ExampleHomeState extends State<_ExampleHome> {
_PhysicsKind selectedPhysics = _PhysicsKind.bouncingSnapping;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
buildOptions(),
_MySheet(physicsKind: selectedPhysics),
],
),
);
}
Widget buildOptions() {
return SafeArea(
child: Column(
children: [
for (final physics in _PhysicsKind.values)
RadioListTile(
title: Text(physics.name),
value: physics,
groupValue: selectedPhysics,
onChanged: (value) => setState(() {
selectedPhysics = value!;
}),
),
],
),
);
}
}
// Height of the sheet in logical pixels.
const _sheetHeight = 500.0;
// Fraction of the sheet height that the sheet should snap to.
// Must be between 0 and 1.
const _halfwayFraction = 0.6;
class _MySheet extends StatelessWidget {
const _MySheet({
required this.physicsKind,
});
final _PhysicsKind physicsKind;
SheetPhysics createPhysics(_PhysicsKind kind) {
// With this configuration, the sheet will snap to:
// - the position at which ony (_halfwayFraction * 100)% of the content is visible, or
// - the position at which the entire content is visible.
// Note that the "position" is the visible height of the sheet.
const snappingPhysics = SnappingSheetPhysics(
behavior: SnapToNearest(
anchors: [
SheetAnchor.proportional(_halfwayFraction),
SheetAnchor.proportional(1),
],
),
// Tips: The above configuration can be replaced with a 'SnapToNearestEdge',
// which will snap to either the 'minPosition' or 'maxPosition' of the sheet:
// snappingBehavior: const SnapToNearestEdge(),
);
return switch (kind) {
_PhysicsKind.clamping => const ClampingSheetPhysics(),
_PhysicsKind.bouncing => const BouncingSheetPhysics(),
_PhysicsKind.clampingSnapping =>
// Use 'parent' to combine multiple physics behaviors.
const ClampingSheetPhysics(parent: snappingPhysics),
_PhysicsKind.bouncingSnapping =>
const BouncingSheetPhysics(parent: snappingPhysics),
};
}
@override
Widget build(BuildContext context) {
return DraggableSheet(
// The 'minPosition' and 'maxPosition' properties determine
// how far the sheet can be dragged. Note that "position"
// refers to the visible height of the sheet. For example,
// the configuration below ensures that the sheet is fully visible
// at first and can then be dragged down to (_halfwayFraction * 100)%
// of the sheet height at minimum.
minPosition: const SheetAnchor.proportional(_halfwayFraction),
maxPosition: const SheetAnchor.proportional(1),
// Default
initialPosition: const SheetAnchor.proportional(1),
// Default
// 'physics' determines how the sheet will behave when the user reaches
// the maximum or minimum position, or when the user stops dragging.
physics: createPhysics(physicsKind),
child: buildContent(context),
);
}
Widget buildContent(BuildContext context) {
return SizedBox(
width: double.infinity,
height: _sheetHeight,
child: Material(
color: Theme.of(context).colorScheme.secondaryContainer,
borderRadius: BorderRadius.circular(16),
clipBehavior: Clip.antiAlias,
child: Column(
children: [
Flexible(
fit: FlexFit.tight,
flex: (_halfwayFraction * 10).toInt(),
child: Container(
color: Theme.of(context).colorScheme.secondaryContainer,
alignment: Alignment.center,
child: Text(
'${(_halfwayFraction * 100).toInt()}%',
style: Theme.of(context).textTheme.displaySmall?.copyWith(
color:
Theme.of(context).colorScheme.onSecondaryContainer,
),
),
),
),
Flexible(
fit: FlexFit.tight,
flex: (10 - _halfwayFraction * 10).toInt(),
child: Container(
color: Theme.of(context).colorScheme.tertiary,
alignment: Alignment.center,
child: Text(
'${(100 - _halfwayFraction * 100).toInt()}%',
style: Theme.of(context).textTheme.displaySmall?.copyWith(
color: Theme.of(context).colorScheme.onTertiary,
),
),
),
),
],
),
),
);
}
}