Skip to content

Commit c2a0170

Browse files
Adjust styling on Attachments bottom sheet (#409)
* re-work styling * use a non-modal bottom sheet * copilot feedback * simplify selection * update when moving directly from one feature to another * update screenshot
1 parent 2c1b929 commit c2a0170

File tree

2 files changed

+88
-102
lines changed

2 files changed

+88
-102
lines changed

lib/samples/edit_feature_attachments/edit_feature_attachments.dart

Lines changed: 88 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class _EditFeatureAttachmentsState extends State<EditFeatureAttachments>
4141
),
4242
),
4343
);
44+
// The currently selected feature.
45+
ArcGISFeature? _selectedFeature;
4446

4547
@override
4648
Widget build(BuildContext context) {
@@ -50,6 +52,16 @@ class _EditFeatureAttachmentsState extends State<EditFeatureAttachments>
5052
onMapViewReady: onMapViewReady,
5153
onTap: onTap,
5254
),
55+
// If a feature is selected, show the bottom sheet to manage its attachments.
56+
bottomSheet: _selectedFeature == null
57+
? null
58+
: AttachmentsOptions(
59+
arcGISFeature: _selectedFeature!,
60+
applyEdits: _applyEdits,
61+
onCloseIconPressed: () {
62+
setState(() => _selectedFeature = null);
63+
},
64+
),
5365
);
5466
}
5567

@@ -80,27 +92,15 @@ class _EditFeatureAttachmentsState extends State<EditFeatureAttachments>
8092

8193
// If there are features identified, show the bottom sheet to display the
8294
// attachment information for the selected feature.
83-
final features = identifyLayerResult.geoElements
84-
.whereType<Feature>()
85-
.toList();
86-
if (features.isNotEmpty) {
87-
_featureLayer.selectFeatures(features);
88-
final selectedFeature = features.first as ArcGISFeature;
89-
if (mounted) _showBottomSheet(selectedFeature);
95+
final feature = identifyLayerResult.geoElements
96+
.whereType<ArcGISFeature>()
97+
.firstOrNull;
98+
setState(() => _selectedFeature = feature);
99+
if (feature != null) {
100+
_featureLayer.selectFeatures([feature]);
90101
}
91102
}
92103

93-
// Show the bottom sheet to display the attachment information.
94-
void _showBottomSheet(ArcGISFeature selectedFeature) {
95-
showModalBottomSheet<void>(
96-
context: context,
97-
builder: (context) => AttachmentsOptions(
98-
arcGISFeature: selectedFeature,
99-
applyEdits: _applyEdits,
100-
),
101-
);
102-
}
103-
104104
// Apply the changes to the feature table.
105105
Future<void> _applyEdits(ArcGISFeature selectedFeature) async {
106106
final serviceFeatureTable =
@@ -124,10 +124,12 @@ class AttachmentsOptions extends StatefulWidget {
124124
const AttachmentsOptions({
125125
required this.arcGISFeature,
126126
required this.applyEdits,
127+
required this.onCloseIconPressed,
127128
super.key,
128129
});
129130
final ArcGISFeature arcGISFeature;
130131
final FutureOr<void> Function(ArcGISFeature) applyEdits;
132+
final void Function() onCloseIconPressed;
131133

132134
@override
133135
State<AttachmentsOptions> createState() => _AttachmentsOptionsState();
@@ -136,104 +138,88 @@ class AttachmentsOptions extends StatefulWidget {
136138
// State class for the AttachmentsOptions.
137139
class _AttachmentsOptionsState extends State<AttachmentsOptions>
138140
with SampleStateSupport {
139-
late final String _damageType;
141+
String? _damageType;
140142
var _attachments = <Attachment>[];
141-
var _isLoading = false;
143+
var _isLoading = true;
142144

143145
@override
144146
void initState() {
145147
super.initState();
146-
_damageType = widget.arcGISFeature.attributes['typdamage'] as String? ?? '';
148+
_damageType = widget.arcGISFeature.attributes['typdamage'] as String?;
147149
_loadAttachments();
148150
}
149151

152+
@override
153+
void didUpdateWidget(covariant AttachmentsOptions oldWidget) {
154+
super.didUpdateWidget(oldWidget);
155+
156+
// If the selected feature has changed, reload the attachments.
157+
if (oldWidget.arcGISFeature.attributes['objectId'] !=
158+
widget.arcGISFeature.attributes['objectId']) {
159+
_attachments = [];
160+
_isLoading = true;
161+
_damageType = widget.arcGISFeature.attributes['typdamage'] as String?;
162+
_loadAttachments();
163+
}
164+
}
165+
150166
@override
151167
Widget build(BuildContext context) {
152-
return Stack(
153-
children: [
154-
Column(
155-
children: [
156-
// Display the damage type and a close button.
157-
Container(
158-
color: Colors.purple,
159-
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
160-
child: Row(
161-
mainAxisAlignment: MainAxisAlignment.spaceBetween,
162-
children: [
163-
Text(
164-
'Damage Type: $_damageType',
165-
style: const TextStyle(
166-
fontSize: 18,
167-
fontWeight: FontWeight.bold,
168-
color: Colors.white,
168+
// Present a bottom sheet with attachment options.
169+
return BottomSheetSettings(
170+
onCloseIconPressed: widget.onCloseIconPressed,
171+
title: 'Damage Type: $_damageType',
172+
settingsWidgets: (context) => [
173+
Container(
174+
constraints: BoxConstraints(
175+
maxHeight: MediaQuery.sizeOf(context).height * 0.5,
176+
),
177+
child: SingleChildScrollView(
178+
child: Column(
179+
children: [
180+
Row(
181+
children: [
182+
// Display the number of attachments.
183+
const Text('Number of Attachments: '),
184+
if (_isLoading)
185+
const SizedBox(
186+
height: 18,
187+
width: 18,
188+
child: CircularProgressIndicator(),
189+
)
190+
else
191+
Text('${_attachments.length}'),
192+
const Spacer(),
193+
// A button to add an attachment.
194+
ElevatedButton(
195+
onPressed: addAttachment,
196+
child: const Text('Add Attachment'),
169197
),
170-
),
171-
if (_isLoading)
172-
const SizedBox(
173-
height: 18,
174-
width: 18,
175-
child: CircularProgressIndicator(color: Colors.white),
176-
)
177-
else
178-
const SizedBox.shrink(),
179-
IconButton(
180-
icon: const Icon(Icons.close),
181-
onPressed: () => Navigator.pop(context),
182-
),
183-
],
184-
),
185-
),
186-
187-
// Display the number of attachments.
188-
Padding(
189-
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
190-
child: Row(
191-
mainAxisAlignment: MainAxisAlignment.spaceBetween,
192-
children: [
193-
Text('Number of Attachments: ${_attachments.length}'),
194-
ElevatedButton(
195-
onPressed: addAttachment,
196-
child: const Text('Add Attachment'),
197-
),
198-
],
199-
),
200-
),
201-
const Divider(color: Colors.purple),
202-
203-
// Display each attachment with view and delete buttons.
204-
SingleChildScrollView(
205-
child: Column(
206-
children: [
207-
ListView.builder(
208-
itemCount: _attachments.length,
209-
shrinkWrap: true,
210-
padding: const EdgeInsets.all(2),
211-
itemBuilder: (context, index) {
212-
return ListTile(
213-
title: Text(_attachments[index].name),
214-
subtitle: Text(_attachments[index].contentType),
215-
trailing: Row(
216-
mainAxisSize: MainAxisSize.min,
217-
children: [
218-
IconButton(
219-
icon: const Icon(Icons.remove_red_eye),
220-
onPressed: () =>
221-
viewAttachment(_attachments[index]),
222-
),
223-
IconButton(
224-
icon: const Icon(Icons.delete),
225-
onPressed: () =>
226-
deleteAttachment(_attachments[index]),
227-
),
228-
],
198+
],
199+
),
200+
// Display each attachment with view and delete buttons.
201+
..._attachments.map(
202+
(attachment) => ListTile(
203+
title: Text(attachment.name),
204+
subtitle: Text(attachment.contentType),
205+
trailing: Row(
206+
mainAxisSize: MainAxisSize.min,
207+
children: [
208+
IconButton(
209+
icon: const Icon(Icons.remove_red_eye),
210+
onPressed: () => viewAttachment(attachment),
211+
),
212+
IconButton(
213+
icon: const Icon(Icons.delete),
214+
onPressed: () => deleteAttachment(attachment),
229215
),
230-
);
231-
},
216+
],
217+
),
232218
),
233-
],
234-
),
219+
),
220+
],
235221
),
236-
],
222+
),
237223
),
238224
],
239225
);
92.1 KB
Loading

0 commit comments

Comments
 (0)