mirror of
https://github.com/foss42/apidash.git
synced 2025-11-30 17:59:18 +08:00
feat: add a dedicated ScrollController shared between the Scrollbar and ListView inside
This commit is contained in:
@@ -42,113 +42,120 @@ Future<List<OpenApiOperationItem>?> showOpenApiOperationPickerDialog({
|
||||
bool selectAll = ops.isNotEmpty;
|
||||
String searchQuery = '';
|
||||
|
||||
return showDialog<List<OpenApiOperationItem>>(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
builder: (ctx) {
|
||||
return StatefulBuilder(builder: (ctx, setState) {
|
||||
// Filter operations based on search query
|
||||
final filteredOps = <int>[];
|
||||
for (int i = 0; i < ops.length; i++) {
|
||||
final o = ops[i];
|
||||
final label = '${o.method} ${o.path}'.toLowerCase();
|
||||
if (searchQuery.isEmpty ||
|
||||
label.contains(searchQuery.toLowerCase())) {
|
||||
filteredOps.add(i);
|
||||
final scrollController = ScrollController();
|
||||
try {
|
||||
return await showDialog<List<OpenApiOperationItem>>(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
builder: (ctx) {
|
||||
return StatefulBuilder(builder: (ctx, setState) {
|
||||
// Filter operations based on search query
|
||||
final filteredOps = <int>[];
|
||||
for (int i = 0; i < ops.length; i++) {
|
||||
final o = ops[i];
|
||||
final label = '${o.method} ${o.path}'.toLowerCase();
|
||||
if (searchQuery.isEmpty ||
|
||||
label.contains(searchQuery.toLowerCase())) {
|
||||
filteredOps.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AlertDialog(
|
||||
title: Text('Import from $title'),
|
||||
content: SizedBox(
|
||||
width: 520,
|
||||
height: 420,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// TODO: Create a common Search field widget
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: TextField(
|
||||
onChanged: (value) {
|
||||
return AlertDialog(
|
||||
title: Text('Import from $title'),
|
||||
content: SizedBox(
|
||||
width: 520,
|
||||
height: 420,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// TODO: Create a common Search field widget
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: TextField(
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
searchQuery = value;
|
||||
});
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Search routes',
|
||||
hintText: 'Type to filter routes...',
|
||||
prefixIcon: Icon(Icons.search),
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Select all checkbox
|
||||
CheckboxListTile(
|
||||
value: selectAll,
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
searchQuery = value;
|
||||
selectAll = v ?? false;
|
||||
selected
|
||||
..clear()
|
||||
..addAll(selectAll
|
||||
? List<int>.generate(ops.length, (i) => i)
|
||||
: const <int>{});
|
||||
});
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Search routes',
|
||||
hintText: 'Type to filter routes...',
|
||||
prefixIcon: Icon(Icons.search),
|
||||
border: OutlineInputBorder(),
|
||||
title: const Text('Select all'),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
),
|
||||
// Routes list
|
||||
Expanded(
|
||||
child: Scrollbar(
|
||||
controller: scrollController,
|
||||
thumbVisibility: true,
|
||||
child: ListView.builder(
|
||||
controller: scrollController,
|
||||
itemCount: filteredOps.length,
|
||||
itemBuilder: (c, index) {
|
||||
final i = filteredOps[index];
|
||||
final o = ops[i];
|
||||
final label = '${o.method} ${o.path}';
|
||||
final checked = selected.contains(i);
|
||||
return CheckboxListTile(
|
||||
value: checked,
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
if (v == true) {
|
||||
selected.add(i);
|
||||
} else {
|
||||
selected.remove(i);
|
||||
}
|
||||
selectAll = selected.length == ops.length;
|
||||
});
|
||||
},
|
||||
title: Text(label),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Select all checkbox
|
||||
CheckboxListTile(
|
||||
value: selectAll,
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
selectAll = v ?? false;
|
||||
selected
|
||||
..clear()
|
||||
..addAll(selectAll
|
||||
? List<int>.generate(ops.length, (i) => i)
|
||||
: const <int>{});
|
||||
});
|
||||
},
|
||||
title: const Text('Select all'),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
),
|
||||
// Routes list
|
||||
Expanded(
|
||||
child: Scrollbar(
|
||||
thumbVisibility: true,
|
||||
child: ListView.builder(
|
||||
itemCount: filteredOps.length,
|
||||
itemBuilder: (c, index) {
|
||||
final i = filteredOps[index];
|
||||
final o = ops[i];
|
||||
final label = '${o.method} ${o.path}';
|
||||
final checked = selected.contains(i);
|
||||
return CheckboxListTile(
|
||||
value: checked,
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
if (v == true) {
|
||||
selected.add(i);
|
||||
} else {
|
||||
selected.remove(i);
|
||||
}
|
||||
selectAll = selected.length == ops.length;
|
||||
});
|
||||
},
|
||||
title: Text(label),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
);
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(null),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: selected.isEmpty
|
||||
? null
|
||||
: () {
|
||||
final result = selected.map((i) => ops[i]).toList();
|
||||
Navigator.of(ctx).pop(result);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(null),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: selected.isEmpty
|
||||
? null
|
||||
: () {
|
||||
final result = selected.map((i) => ops[i]).toList();
|
||||
Navigator.of(ctx).pop(result);
|
||||
},
|
||||
child: const Text('Import'),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
child: const Text('Import'),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
scrollController.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user