Preserve Flutter mirror-os codebase
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mirror_ui/mirror_ui.dart';
|
||||
import '../providers/ha_provider.dart';
|
||||
|
||||
class HaEntityPickerScreen extends ConsumerStatefulWidget {
|
||||
final List<String> selectedIds;
|
||||
|
||||
const HaEntityPickerScreen({super.key, this.selectedIds = const []});
|
||||
|
||||
@override
|
||||
ConsumerState<HaEntityPickerScreen> createState() => _HaEntityPickerScreenState();
|
||||
}
|
||||
|
||||
class _HaEntityPickerScreenState extends ConsumerState<HaEntityPickerScreen> {
|
||||
late Set<String> _selected;
|
||||
String _search = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selected = Set.from(widget.selectedIds);
|
||||
}
|
||||
|
||||
String _domainOf(String entityId) {
|
||||
final dotIndex = entityId.indexOf('.');
|
||||
return dotIndex > 0 ? entityId.substring(0, dotIndex) : 'other';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entitiesAsync = ref.watch(haEntitiesProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Select HA Entities'),
|
||||
backgroundColor: MirrorTheme.background,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(_selected.toList()),
|
||||
child: Text('Done (${_selected.length})'),
|
||||
),
|
||||
],
|
||||
),
|
||||
backgroundColor: MirrorTheme.background,
|
||||
body: entitiesAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => Center(child: Text('Error: $e')),
|
||||
data: (state) {
|
||||
if (state.error != null) {
|
||||
return Center(child: Text(state.error!));
|
||||
}
|
||||
return _buildList(state.entities);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildList(List entities) {
|
||||
// Filter by search
|
||||
final filtered = entities.where((e) {
|
||||
final id = e.entityId as String;
|
||||
final name =
|
||||
(e.attributes['friendly_name'] as String?) ?? id;
|
||||
final query = _search.toLowerCase();
|
||||
return id.toLowerCase().contains(query) || name.toLowerCase().contains(query);
|
||||
}).toList();
|
||||
|
||||
// Group by domain
|
||||
final grouped = <String, List<dynamic>>{};
|
||||
for (final entity in filtered) {
|
||||
final domain = _domainOf(entity.entityId as String);
|
||||
grouped.putIfAbsent(domain, () => []).add(entity);
|
||||
}
|
||||
|
||||
final sortedDomains = grouped.keys.toList()..sort();
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search entities...',
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
filled: true,
|
||||
fillColor: MirrorTheme.surface,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
),
|
||||
onChanged: (v) => setState(() => _search = v),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: sortedDomains.length,
|
||||
itemBuilder: (context, domainIndex) {
|
||||
final domain = sortedDomains[domainIndex];
|
||||
final items = grouped[domain]!;
|
||||
return ExpansionTile(
|
||||
title: Text(
|
||||
domain,
|
||||
style: TextStyle(color: MirrorTheme.accent, fontWeight: FontWeight.w600),
|
||||
),
|
||||
initiallyExpanded: items.any((e) => _selected.contains(e.entityId)),
|
||||
children: items.map((entity) {
|
||||
final id = entity.entityId as String;
|
||||
final name =
|
||||
(entity.attributes['friendly_name'] as String?) ?? id;
|
||||
return CheckboxListTile(
|
||||
title: Text(name),
|
||||
subtitle: Text(id, style: const TextStyle(fontSize: 12)),
|
||||
value: _selected.contains(id),
|
||||
activeColor: MirrorTheme.accent,
|
||||
onChanged: (checked) {
|
||||
setState(() {
|
||||
if (checked == true) {
|
||||
_selected.add(id);
|
||||
} else {
|
||||
_selected.remove(id);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user