Skip to content

Commit aecc1d4

Browse files
committed
Improve dialog usage
1 parent 3675752 commit aecc1d4

File tree

7 files changed

+249
-130
lines changed

7 files changed

+249
-130
lines changed

lib/feature/sound/modal/section/base_section.dart

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class BaseSection extends StatelessWidget {
4343
/// ```
4444
const BaseSection({
4545
required this.child,
46+
this.title,
47+
this.padding,
4648
super.key,
4749
});
4850

@@ -52,6 +54,12 @@ class BaseSection extends StatelessWidget {
5254
/// The child will be wrapped with consistent padding and background styling.
5355
final Widget child;
5456

57+
/// Optional title shown above [child]. If null, only [child] is rendered.
58+
final String? title;
59+
60+
/// Optional padding override. Defaults to EdgeInsets.all(20).
61+
final EdgeInsetsGeometry? padding;
62+
5563
@override
5664
Widget build(BuildContext context) {
5765
// Extract the surface variant color from the current theme
@@ -63,8 +71,21 @@ class BaseSection extends StatelessWidget {
6371
color: surfaceVariant,
6472
borderRadius: BorderRadius.circular(16),
6573
),
66-
padding: const EdgeInsets.all(20),
67-
child: child,
74+
padding: padding ?? const EdgeInsets.all(20),
75+
child: title == null
76+
? child
77+
: Column(
78+
crossAxisAlignment: CrossAxisAlignment.start,
79+
mainAxisSize: MainAxisSize.min,
80+
children: [
81+
Text(
82+
title!,
83+
style: Theme.of(context).textTheme.titleSmall,
84+
),
85+
const SizedBox(height: 12),
86+
child,
87+
],
88+
),
6889
);
6990
}
7091
}

lib/feature/sound/modal/section/sound_slider.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:flutter/material.dart';
2-
import 'package:stelaris/feature/sound/modal/section/base_section.dart';
32

43
/// A widget that combines a descriptive [Text] label with a [Slider].
54
///

lib/feature/sound/modal/section/string_field_section.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class _StringInputSectionState extends State<StringInputSection> {
1515
@override
1616
void initState() {
1717
super.initState();
18-
this._controller = TextEditingController();
18+
_controller = TextEditingController();
1919
}
2020

2121
@override
@@ -28,6 +28,7 @@ class _StringInputSectionState extends State<StringInputSection> {
2828
),
2929
);
3030
return BaseSection(
31+
title: 'Name',
3132
child: TextField(
3233
controller: _controller,
3334
decoration: InputDecoration(
@@ -36,7 +37,7 @@ class _StringInputSectionState extends State<StringInputSection> {
3637
border: outlineBorder,
3738
suffixIcon: const Tooltip(
3839
message: 'test',
39-
child: const Icon(Icons.info_outline),
40+
child: Icon(Icons.info_outline),
4041
),
4142
enabledBorder: outlineBorder,
4243
focusedBorder: OutlineInputBorder(

lib/feature/sound/modal/sound_file_modal.dart

Lines changed: 104 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -42,93 +42,115 @@ class _SoundFileModalState extends State<SoundFileModal> {
4242

4343
@override
4444
Widget build(BuildContext context) {
45-
final surfaceVariant = Theme.of(
46-
context,
47-
).colorScheme.surfaceContainerHighest;
45+
final media = MediaQuery.of(context);
46+
final isDense = media.size.height < 500;
47+
final smallGap = isDense ? 12.0 : 16.0;
48+
final largeGap = isDense ? 20.0 : 32.0;
49+
4850
return Dialog(
4951
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
50-
child: Padding(
51-
padding: const EdgeInsets.all(24),
52-
child: Form(
53-
key: _formKey,
54-
child: SingleChildScrollView(
55-
child: Column(
56-
mainAxisSize: MainAxisSize.min,
57-
crossAxisAlignment: CrossAxisAlignment.start,
58-
children: [
59-
Text(
60-
widget.create ? 'Create Sound' : 'Edit Sound',
61-
style: Theme.of(context).textTheme.titleLarge,
62-
),
63-
const SizedBox(height: 24),
64-
const StringInputSection(),
65-
const SizedBox(height: 16),
66-
VolumeSection(
67-
initialPitch: _pitch,
68-
initialVolume: _volume,
69-
onPitchFinalized: (v) => setState(() => _pitch = v),
70-
onVolumeFinalized: (v) => setState(() => _volume = v),
71-
),
72-
const SizedBox(height: 16),
73-
IntegerFieldsSection(
74-
weight: _weight,
75-
attenuation: _attenuationDistance,
76-
onWeightChanged: (v) => setState(() => _weight = v),
77-
onAttenuationChanged: (v) =>
78-
setState(() => _attenuationDistance = v),
79-
),
80-
const SizedBox(height: 16),
81-
// Section 3: Switches
82-
SwitchesSection(
83-
streamValue: _stream,
84-
onStreamChanged: (v) => setState(() => _stream = v),
85-
preloadValue: _preload,
86-
onPreloadChanged: (v) => setState(() => _preload = v),
87-
backgroundColor: surfaceVariant, // Pass the color
88-
),
89-
const SizedBox(height: 16),
90-
// Section 4: Type dropdown
91-
BaseSection(
92-
child: DropdownButtonFormField<String>(
93-
initialValue: _type,
94-
decoration: const InputDecoration(
95-
labelText: 'Type',
96-
border: OutlineInputBorder(),
52+
child: ConstrainedBox(
53+
constraints: const BoxConstraints(
54+
minWidth: 400,
55+
),
56+
child: Padding(
57+
padding: const EdgeInsets.all(24),
58+
child: Form(
59+
key: _formKey,
60+
child: SingleChildScrollView(
61+
child: Column(
62+
mainAxisSize: MainAxisSize.min,
63+
crossAxisAlignment: CrossAxisAlignment.start,
64+
children: [
65+
Text(
66+
widget.create ? 'Create Sound' : 'Edit Sound',
67+
style: Theme.of(context).textTheme.titleLarge,
68+
),
69+
const SizedBox(height: 24),
70+
const StringInputSection(),
71+
const SizedBox(height: 16),
72+
VolumeSection(
73+
initialPitch: _pitch,
74+
initialVolume: _volume,
75+
onPitchFinalized: (v) => setState(() => _pitch = v),
76+
onVolumeFinalized: (v) => setState(() => _volume = v),
77+
),
78+
SizedBox(height: smallGap),
79+
IntegerFieldsSection(
80+
weight: _weight,
81+
attenuation: _attenuationDistance,
82+
onWeightChanged: (v) => setState(() => _weight = v),
83+
onAttenuationChanged: (v) =>
84+
setState(() => _attenuationDistance = v),
85+
dense: isDense,
86+
),
87+
SizedBox(height: smallGap),
88+
// Combined Section: Switches + Type dropdown
89+
BaseSection(
90+
title: 'Options',
91+
child: Column(
92+
mainAxisSize: MainAxisSize.min,
93+
crossAxisAlignment: CrossAxisAlignment.stretch,
94+
children: [
95+
LayoutBuilder(
96+
builder: (context, constraints) {
97+
final horizontal = constraints.maxWidth >= 420;
98+
return SwitchesSection(
99+
streamValue: _stream,
100+
onStreamChanged: (v) => setState(() => _stream = v),
101+
preloadValue: _preload,
102+
onPreloadChanged: (v) => setState(() => _preload = v),
103+
wrapInBaseSection: false,
104+
vertical: !horizontal,
105+
);
106+
},
107+
),
108+
SizedBox(height: smallGap),
109+
const Divider(height: 1),
110+
SizedBox(height: smallGap),
111+
DropdownButtonFormField<String>(
112+
initialValue: _type,
113+
decoration: const InputDecoration(
114+
labelText: 'Type',
115+
border: OutlineInputBorder(),
116+
),
117+
items: const [
118+
DropdownMenuItem(value: 'file', child: Text('File')),
119+
DropdownMenuItem(value: 'event', child: Text('Event')),
120+
],
121+
onChanged: (v) => setState(() => _type = v ?? 'file'),
122+
),
123+
],
97124
),
98-
items: const [
99-
DropdownMenuItem(value: 'file', child: Text('File')),
100-
DropdownMenuItem(value: 'event', child: Text('Event')),
125+
),
126+
SizedBox(height: largeGap),
127+
Row(
128+
mainAxisAlignment: MainAxisAlignment.end,
129+
children: [
130+
CancelButton(callback: () => Navigator.pop(context)),
131+
const SizedBox(width: 12),
132+
FilledButton(
133+
onPressed: () {
134+
if (_formKey.currentState?.validate() ?? false) {
135+
widget.onSave?.call(
136+
name: _name,
137+
volume: _volume,
138+
pitch: _pitch,
139+
weight: _weight,
140+
stream: _stream,
141+
attenuationDistance: _attenuationDistance,
142+
preload: _preload,
143+
type: _type,
144+
);
145+
Navigator.pop(context);
146+
}
147+
},
148+
child: const Text('Save'),
149+
),
101150
],
102-
onChanged: (v) => setState(() => _type = v ?? 'file'),
103151
),
104-
),
105-
const SizedBox(height: 32),
106-
Row(
107-
mainAxisAlignment: MainAxisAlignment.end,
108-
children: [
109-
CancelButton(callback: () => Navigator.pop(context)),
110-
const SizedBox(width: 12),
111-
FilledButton(
112-
onPressed: () {
113-
if (_formKey.currentState?.validate() ?? false) {
114-
widget.onSave?.call(
115-
name: _name,
116-
volume: _volume,
117-
pitch: _pitch,
118-
weight: _weight,
119-
stream: _stream,
120-
attenuationDistance: _attenuationDistance,
121-
preload: _preload,
122-
type: _type,
123-
);
124-
Navigator.pop(context);
125-
}
126-
},
127-
child: const Text('Save'),
128-
),
129-
],
130-
),
131-
],
152+
],
153+
),
132154
),
133155
),
134156
),

lib/feature/sound/modal/type/integer_fields_section.dart

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,68 @@ class IntegerFieldsSection extends StatelessWidget {
88
final int minValue;
99
final ValueChanged<int> onWeightChanged;
1010
final ValueChanged<int> onAttenuationChanged;
11+
final bool dense;
1112

1213
const IntegerFieldsSection({
1314
required this.weight,
1415
required this.attenuation,
1516
required this.onWeightChanged,
1617
required this.onAttenuationChanged,
1718
this.minValue = 1,
19+
this.dense = false,
1820
super.key,
1921
});
2022

2123
@override
2224
Widget build(BuildContext context) {
25+
final verticalGap = dense ? 8.0 : 12.0;
2326
return BaseSection(
24-
child: Column(
25-
children: [
26-
BaseIntegerField(
27-
label: 'Weight',
28-
initialValue: weight,
29-
minValue: minValue,
30-
onChanged: onWeightChanged,
31-
),
32-
const SizedBox(height: 12),
33-
BaseIntegerField(
34-
label: 'Attenuation Distance',
35-
initialValue: attenuation,
36-
minValue: minValue,
37-
onChanged: onAttenuationChanged,
38-
),
39-
],
27+
title: 'Weight & Attenuation',
28+
padding: dense ? const EdgeInsets.all(16) : null,
29+
child: LayoutBuilder(
30+
builder: (context, constraints) {
31+
final canRow = !dense && constraints.maxWidth >= 520;
32+
if (canRow) {
33+
return Row(
34+
children: [
35+
Expanded(
36+
child: BaseIntegerField(
37+
label: 'Weight',
38+
initialValue: weight,
39+
minValue: minValue,
40+
onChanged: onWeightChanged,
41+
),
42+
),
43+
const SizedBox(width: 16),
44+
Expanded(
45+
child: BaseIntegerField(
46+
label: 'Attenuation Distance',
47+
initialValue: attenuation,
48+
minValue: minValue,
49+
onChanged: onAttenuationChanged,
50+
),
51+
),
52+
],
53+
);
54+
}
55+
return Column(
56+
children: [
57+
BaseIntegerField(
58+
label: 'Weight',
59+
initialValue: weight,
60+
minValue: minValue,
61+
onChanged: onWeightChanged,
62+
),
63+
SizedBox(height: verticalGap),
64+
BaseIntegerField(
65+
label: 'Attenuation Distance',
66+
initialValue: attenuation,
67+
minValue: minValue,
68+
onChanged: onAttenuationChanged,
69+
),
70+
],
71+
);
72+
},
4073
),
4174
);
4275
}

0 commit comments

Comments
 (0)