import 'package:apidash_core/apidash_core.dart'; import 'package:flutter/material.dart'; typedef OpenApiOperationItem = ({String method, String path, Operation op}); Future?> showOpenApiOperationPickerDialog({ required BuildContext context, required OpenApi spec, String? sourceName, }) async { final title = (spec.info.title.trim().isNotEmpty ? spec.info.title.trim() : (sourceName ?? 'OpenAPI')) .trim(); final ops = []; (spec.paths ?? const {}).forEach((path, item) { final map = { 'GET': item.get, 'POST': item.post, 'PUT': item.put, 'DELETE': item.delete, 'PATCH': item.patch, 'HEAD': item.head, 'OPTIONS': item.options, 'TRACE': item.trace, }; map.forEach((method, op) { if (op != null) { ops.add((method: method, path: path, op: op)); } }); }); if (ops.isEmpty) { // Nothing to select; return empty selection. return []; } // Multi-select: default select all final selected = {for (var i = 0; i < ops.length; i++) i}; bool selectAll = ops.isNotEmpty; String searchQuery = ''; final scrollController = ScrollController(); try { return await showDialog>( context: context, useRootNavigator: true, builder: (ctx) { return StatefulBuilder(builder: (ctx, setState) { // Filter operations based on search query final filteredOps = []; 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) { 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(() { selectAll = v ?? false; selected ..clear() ..addAll(selectAll ? List.generate(ops.length, (i) => i) : const {}); }); }, 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, ); }, ), ), ), ], ), ), 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'), ), ], ); }); }, ); } finally { scrollController.dispose(); } }