Skip to content

Commit 943433c

Browse files
author
Christoph Bühler
committed
feat: add openOnRender flag
1 parent eb57b6e commit 943433c

File tree

2 files changed

+159
-74
lines changed

2 files changed

+159
-74
lines changed

lib/src/generic_dropdown.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,18 @@ class GenericDropdown extends StatefulWidget {
9292
/// outside the content container. Defaults to `true`.
9393
final bool closeOnOutsideTap;
9494

95+
/// Whether the content (dropdown) should be opened on render.
96+
/// Defaults to `false`.
97+
final bool openOnRender;
98+
9599
const GenericDropdown(
96100
{super.key,
97101
required this.contentBuilder,
98102
this.anchor = DropdownAnchor.bottomLeft,
99103
this.direction = DropdownDirection.downRight,
100104
required this.toggleBuilder,
101105
this.closeOnOutsideTap = true,
106+
this.openOnRender = false,
102107
this.offset = Offset.zero});
103108

104109
@override
@@ -116,6 +121,14 @@ class _GenericDropdownState extends State<GenericDropdown> {
116121
super.dispose();
117122
}
118123

124+
@override
125+
void initState() {
126+
if (widget.openOnRender) {
127+
WidgetsBinding.instance.addPostFrameCallback((_) => _open(context));
128+
}
129+
super.initState();
130+
}
131+
119132
RenderBox? _ancestor(BuildContext context) =>
120133
GenericDropdownConfigProvider.of(context)?.rootScreenKey?.currentContext?.findRenderObject() as RenderBox?;
121134

storybook/lib/main.dart

Lines changed: 146 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
22
import 'package:generic_dropdown_widget/generic_dropdown_widget.dart';
33
import 'package:storybook_flutter/storybook_flutter.dart';
44

5-
void main() {
6-
final rootKey = GlobalKey();
5+
final rootKey = GlobalKey();
76

7+
void main() {
88
runApp(Storybook(
99
plugins: initializePlugins(
1010
contentsSidePanel: true,
@@ -17,79 +17,151 @@ void main() {
1717
darkTheme: ThemeData.light(),
1818
debugShowCheckedModeBanner: false,
1919
useInheritedMediaQuery: true,
20-
home: Scaffold(body: SafeArea(child: Center(child: child)))),
21-
initialStory: 'Generic Dropdown',
20+
home: Scaffold(
21+
body: SafeArea(
22+
child: GenericDropdownConfigProvider(
23+
rootScreenKey: rootKey, child: Center(key: UniqueKey(), child: child))))),
24+
// initialStory: 'Generic Dropdown',
2225
stories: [
23-
Story(
24-
name: 'Generic Dropdown',
25-
description: 'A generic dropdown with arbirary toggle and content.',
26-
builder: (context) {
27-
var repaintCount = 1;
28-
29-
return GenericDropdownConfigProvider(
30-
rootScreenKey: rootKey,
31-
child: Align(
32-
alignment: context.knobs.options(
33-
label: 'Position',
34-
initial: Alignment.center,
35-
description:
36-
'The position of the dropdown in this story, to test the content of the dropdown not going off-screen.',
37-
options: const [
38-
Option(label: 'Top Left', value: Alignment.topLeft),
39-
Option(label: 'Top Center', value: Alignment.topCenter),
40-
Option(label: 'Top Right', value: Alignment.topRight),
41-
Option(label: 'Center Left', value: Alignment.centerLeft),
42-
Option(label: 'Center', value: Alignment.center),
43-
Option(label: 'Center Right', value: Alignment.centerRight),
44-
Option(label: 'Bottom Left', value: Alignment.bottomLeft),
45-
Option(label: 'Bottom Center', value: Alignment.bottomCenter),
46-
Option(label: 'Bottom Right', value: Alignment.bottomRight),
47-
]),
48-
child: GenericDropdown(
49-
contentBuilder: (context, repaint, close) => Container(
50-
height: 200,
51-
width: 300,
52-
color: Colors.green.withOpacity(.5),
53-
child: Column(
54-
children: [
55-
const Text('Content'),
56-
TextButton(onPressed: close, child: const Text('Close')),
57-
TextButton(
58-
onPressed: () {
59-
repaintCount++;
60-
repaint();
61-
},
62-
child: Text('Repaint (count: $repaintCount)')),
63-
],
64-
),
65-
),
66-
toggleBuilder: (context, isOpen) => Container(
67-
height: 120,
68-
width: 120,
69-
color: isOpen ? Colors.amber.withOpacity(.25) : Colors.blue.withOpacity(.25),
70-
child: Text('Toggle (${isOpen ? 'Open' : 'Closed'})'),
71-
),
72-
offset: Offset(
73-
context.knobs.sliderInt(label: 'X Offset', initial: 0, min: -100, max: 100).toDouble(),
74-
context.knobs.sliderInt(label: 'Y Offset', initial: 0, min: -100, max: 100).toDouble(),
75-
),
76-
anchor: context.knobs.options(
77-
label: 'Anchor',
78-
description: 'The anchor for the content dropdown.',
79-
initial: DropdownAnchor.bottomLeft,
80-
options: DropdownAnchor.values.map((v) => Option(label: v.name, value: v)).toList()),
81-
direction: context.knobs.options(
82-
label: 'Direction',
83-
description: 'The direction where the dropdown should open to.',
84-
initial: DropdownDirection.downRight,
85-
options: DropdownDirection.values.map((v) => Option(label: v.name, value: v)).toList()),
86-
closeOnOutsideTap: context.knobs.boolean(
87-
label: 'Close On Outside Tap',
88-
description:
89-
'Whether the content is closed on an outside tap or only if the content calls close().',
90-
initial: true),
91-
)));
92-
})
26+
_dropdown(),
27+
_openDropdown(),
9328
],
9429
));
9530
}
31+
32+
Story _dropdown() => Story(
33+
name: 'Generic Dropdown',
34+
description: 'A generic dropdown with arbirary toggle and content.',
35+
builder: (context) {
36+
var repaintCount = 1;
37+
38+
return Align(
39+
alignment: context.knobs.options(
40+
label: 'Position',
41+
initial: Alignment.center,
42+
description:
43+
'The position of the dropdown in this story, to test the content of the dropdown not going off-screen.',
44+
options: const [
45+
Option(label: 'Top Left', value: Alignment.topLeft),
46+
Option(label: 'Top Center', value: Alignment.topCenter),
47+
Option(label: 'Top Right', value: Alignment.topRight),
48+
Option(label: 'Center Left', value: Alignment.centerLeft),
49+
Option(label: 'Center', value: Alignment.center),
50+
Option(label: 'Center Right', value: Alignment.centerRight),
51+
Option(label: 'Bottom Left', value: Alignment.bottomLeft),
52+
Option(label: 'Bottom Center', value: Alignment.bottomCenter),
53+
Option(label: 'Bottom Right', value: Alignment.bottomRight),
54+
]),
55+
child: GenericDropdown(
56+
contentBuilder: (context, repaint, close) => Container(
57+
height: 200,
58+
width: 300,
59+
color: Colors.green.withOpacity(.5),
60+
child: Column(
61+
children: [
62+
const Text('Content'),
63+
TextButton(onPressed: close, child: const Text('Close')),
64+
TextButton(
65+
onPressed: () {
66+
repaintCount++;
67+
repaint();
68+
},
69+
child: Text('Repaint (count: $repaintCount)')),
70+
],
71+
),
72+
),
73+
toggleBuilder: (context, isOpen) => Container(
74+
height: 120,
75+
width: 120,
76+
color: isOpen ? Colors.amber.withOpacity(.25) : Colors.blue.withOpacity(.25),
77+
child: Text('Toggle (${isOpen ? 'Open' : 'Closed'})'),
78+
),
79+
offset: Offset(
80+
context.knobs.sliderInt(label: 'X Offset', initial: 0, min: -100, max: 100).toDouble(),
81+
context.knobs.sliderInt(label: 'Y Offset', initial: 0, min: -100, max: 100).toDouble(),
82+
),
83+
anchor: context.knobs.options(
84+
label: 'Anchor',
85+
description: 'The anchor for the content dropdown.',
86+
initial: DropdownAnchor.bottomLeft,
87+
options: DropdownAnchor.values.map((v) => Option(label: v.name, value: v)).toList()),
88+
direction: context.knobs.options(
89+
label: 'Direction',
90+
description: 'The direction where the dropdown should open to.',
91+
initial: DropdownDirection.downRight,
92+
options: DropdownDirection.values.map((v) => Option(label: v.name, value: v)).toList()),
93+
closeOnOutsideTap: context.knobs.boolean(
94+
label: 'Close On Outside Tap',
95+
description: 'Whether the content is closed on an outside tap or only if the content calls close().',
96+
initial: true),
97+
));
98+
});
99+
100+
Story _openDropdown() => Story(
101+
name: 'Generic Dropdown (open)',
102+
description: 'A generic dropdown that opens on render.',
103+
builder: (context) {
104+
var repaintCount = 1;
105+
106+
return Align(
107+
alignment: context.knobs.options(
108+
label: 'Position',
109+
initial: Alignment.center,
110+
description:
111+
'The position of the dropdown in this story, to test the content of the dropdown not going off-screen.',
112+
options: const [
113+
Option(label: 'Top Left', value: Alignment.topLeft),
114+
Option(label: 'Top Center', value: Alignment.topCenter),
115+
Option(label: 'Top Right', value: Alignment.topRight),
116+
Option(label: 'Center Left', value: Alignment.centerLeft),
117+
Option(label: 'Center', value: Alignment.center),
118+
Option(label: 'Center Right', value: Alignment.centerRight),
119+
Option(label: 'Bottom Left', value: Alignment.bottomLeft),
120+
Option(label: 'Bottom Center', value: Alignment.bottomCenter),
121+
Option(label: 'Bottom Right', value: Alignment.bottomRight),
122+
]),
123+
child: GenericDropdown(
124+
openOnRender: true,
125+
contentBuilder: (context, repaint, close) => Container(
126+
height: 200,
127+
width: 300,
128+
color: Colors.green.withOpacity(.5),
129+
child: Column(
130+
children: [
131+
const Text('Content'),
132+
TextButton(onPressed: close, child: const Text('Close')),
133+
TextButton(
134+
onPressed: () {
135+
repaintCount++;
136+
repaint();
137+
},
138+
child: Text('Repaint (count: $repaintCount)')),
139+
],
140+
),
141+
),
142+
toggleBuilder: (context, isOpen) => Container(
143+
height: 120,
144+
width: 120,
145+
color: isOpen ? Colors.amber.withOpacity(.25) : Colors.blue.withOpacity(.25),
146+
child: Text('Toggle (${isOpen ? 'Open' : 'Closed'})'),
147+
),
148+
offset: Offset(
149+
context.knobs.sliderInt(label: 'X Offset', initial: 0, min: -100, max: 100).toDouble(),
150+
context.knobs.sliderInt(label: 'Y Offset', initial: 0, min: -100, max: 100).toDouble(),
151+
),
152+
anchor: context.knobs.options(
153+
label: 'Anchor',
154+
description: 'The anchor for the content dropdown.',
155+
initial: DropdownAnchor.bottomLeft,
156+
options: DropdownAnchor.values.map((v) => Option(label: v.name, value: v)).toList()),
157+
direction: context.knobs.options(
158+
label: 'Direction',
159+
description: 'The direction where the dropdown should open to.',
160+
initial: DropdownDirection.downRight,
161+
options: DropdownDirection.values.map((v) => Option(label: v.name, value: v)).toList()),
162+
closeOnOutsideTap: context.knobs.boolean(
163+
label: 'Close On Outside Tap',
164+
description: 'Whether the content is closed on an outside tap or only if the content calls close().',
165+
initial: true),
166+
));
167+
});

0 commit comments

Comments
 (0)