import 'package:flutter/material.dart'; import '../brew/models.dart'; import 'common.dart'; import 'package_details.dart'; class InstalledPage extends StatefulWidget { const InstalledPage({super.key}); @override State createState() => _InstalledPageState(); } class _InstalledPageState extends State { String _filter = ''; PackageKind? _kind; /// Hide formulae that were only pulled in as dependencies. bool _requestedOnly = false; @override Widget build(BuildContext context) { final state = AppScope.of(context); final q = _filter.toLowerCase(); final packages = state.installed .where((p) => _kind == null || p.kind == _kind) .where((p) => !_requestedOnly || p.installedOnRequest) .where( (p) => q.isEmpty || p.name.toLowerCase().contains(q) || (p.displayName?.toLowerCase().contains(q) ?? false) || (p.desc?.toLowerCase().contains(q) ?? false), ) .toList() ..sort((a, b) => a.name.compareTo(b.name)); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ PageHeader( title: 'Installed', bottom: Row( children: [ Expanded( child: FilterField( onChanged: (v) => setState(() => _filter = v), ), ), const SizedBox(width: 12), SegmentedButton( segments: const [ ButtonSegment(value: null, label: Text('All')), ButtonSegment( value: PackageKind.formula, label: Text('Formulae'), ), ButtonSegment(value: PackageKind.cask, label: Text('Casks')), ], selected: {_kind}, onSelectionChanged: (s) => setState(() => _kind = s.first), ), const SizedBox(width: 12), FilterChip( label: const Text('Requested only'), tooltip: 'Hide formulae installed only as dependencies', selected: _requestedOnly, onSelected: (v) => setState(() => _requestedOnly = v), ), ], ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Text( '${packages.length} of ${state.installed.length}', style: TextStyle(color: Theme.of(context).colorScheme.outline), ), ), Expanded( child: packages.isEmpty ? EmptyState( state.loading ? 'Loading…' : 'Nothing installed matches.', ) : ListView.builder( itemCount: packages.length, itemBuilder: (context, i) => PackageTile(package: packages[i]), ), ), ], ); } } class PackageTile extends StatelessWidget { const PackageTile({super.key, required this.package}); final Package package; @override Widget build(BuildContext context) { final p = package; final scheme = Theme.of(context).colorScheme; return ListTile( leading: Icon(kindIcon(p.kind)), title: Row( children: [ Flexible(child: Text(p.name, overflow: TextOverflow.ellipsis)), if (p.displayName != null && p.displayName != p.name) Padding( padding: const EdgeInsets.only(left: 8), child: Text( p.displayName!, style: TextStyle(color: scheme.outline), ), ), if (p.outdated) Tag('outdated', color: scheme.tertiary), if (p.pinned) const Tag('pinned'), if (p.kind == PackageKind.formula && !p.installedOnRequest) Tag('dependency', color: scheme.outline), ], ), subtitle: p.desc == null ? null : Text(p.desc!, maxLines: 1, overflow: TextOverflow.ellipsis), trailing: Text( p.installedVersion ?? '', style: const TextStyle(fontFamily: 'Menlo', fontSize: 12), ), onTap: () => showPackageDetails(context, p.fullName, p.kind), ); } }