@@ -7,6 +7,7 @@ import 'package:habr_app/widgets/adaptive_ui.dart';
77import 'package:itertools/itertools.dart' ;
88import 'package:hive/hive.dart' ;
99import 'package:flutter_gen/gen_l10n/app_localizations.dart' ;
10+ import 'package:tuple_dart/tuple.dart' ;
1011
1112class FiltersPage extends StatefulWidget {
1213 @override
@@ -28,38 +29,46 @@ class _FiltersPageState extends State<FiltersPage> {
2829 );
2930 }
3031
32+ List <Tuple2 <int , ChildT >>
33+ _filterChildType <ChildT extends Filter <PostPreview >>(
34+ Iterable <Tuple2 <int , Filter <PostPreview >>> filtersWithIndex) {
35+ return filtersWithIndex
36+ .where ((e) => e.item2 is ChildT )
37+ .map ((e) => e.withItem2 (e.item2 as ChildT ))
38+ .toList (growable: false );
39+ }
40+
3141 Widget _buildBody () {
42+ final filtersStore = FiltersStorage ();
43+ final removeFilter = (int i) => () => filtersStore.removeFilterAt (i);
3244 return ValueListenableBuilder <Box <Filter <PostPreview >>>(
33- valueListenable: FiltersStorage ().listenable (),
34- builder: (context, box, child) => ListView (
35- children: box.values
36- .mapIndexed <Widget >((i, filter) {
37- if (filter is NicknameAuthorFilter ) {
38- return ListTile (
39- leading: const Icon (Icons .person_outline),
40- title: Text (filter.nickname! ),
41- trailing: IconButton (
42- icon: const Icon (Icons .clear),
43- onPressed: () => FiltersStorage ().removeFilterAt (i),
44- ),
45- );
46- } else if (filter is CompanyNameFilter ) {
47- return ListTile (
48- leading: const Icon (Icons .groups),
49- title: Text (filter.companyName! ),
50- trailing: IconButton (
51- icon: const Icon (Icons .clear),
52- onPressed: () => FiltersStorage ().removeFilterAt (i),
53- ),
54- );
55- } else {
56- logInfo ("filter not supported" );
57- }
58- return null ;
59- } as Widget Function (int , Filter <PostPreview >))
60- .map ((e) => DefaultConstraints (child: e))
61- .toList (),
62- ),
45+ valueListenable: filtersStore.listenable (),
46+ builder: (context, box, child) {
47+ final filtersWithIndex = box.values.enumerate ().toList ();
48+ final filtersByAuthor =
49+ _filterChildType <NicknameAuthorFilter >(filtersWithIndex);
50+ final filtersByCompany =
51+ _filterChildType <CompanyNameFilter >(filtersWithIndex);
52+
53+ return ListView (
54+ children: [
55+ if (filtersByAuthor.isNotEmpty)
56+ FiltersGroup <NicknameAuthorFilter >(
57+ filters: filtersByAuthor,
58+ leading: const Icon (Icons .person_outline),
59+ titleBuilder: (filter) => Text (filter.nickname),
60+ onRemoveBuilder: removeFilter,
61+ ),
62+ if (filtersByCompany.isNotEmpty)
63+ FiltersGroup <CompanyNameFilter >(
64+ filters: filtersByCompany,
65+ leading: const Icon (Icons .groups),
66+ titleBuilder: (filter) => Text (filter.companyName),
67+ onRemoveBuilder: removeFilter,
68+ ),
69+ ].map ((e) => DefaultConstraints (child: e)).toList (growable: false ),
70+ );
71+ },
6372 );
6473 }
6574
@@ -117,6 +126,40 @@ class _FiltersPageState extends State<FiltersPage> {
117126 }
118127}
119128
129+ class FiltersGroup <FilterT extends Filter <PostPreview >>
130+ extends StatelessWidget {
131+ final Iterable <Tuple2 <int , FilterT >> filters;
132+ final Widget leading;
133+ final Widget Function (FilterT ) titleBuilder;
134+ final void Function () Function (int ) onRemoveBuilder;
135+
136+ FiltersGroup ({
137+ required this .filters,
138+ required this .leading,
139+ required this .titleBuilder,
140+ required this .onRemoveBuilder,
141+ });
142+
143+ @override
144+ Widget build (BuildContext context) {
145+ return Card (
146+ child: Column (
147+ children: filters.map ((tuple) {
148+ final index = tuple.item1;
149+ final filter = tuple.item2;
150+ return ListTile (
151+ leading: leading,
152+ title: titleBuilder (filter),
153+ trailing: IconButton (
154+ icon: const Icon (Icons .clear),
155+ onPressed: onRemoveBuilder (index),
156+ ));
157+ }).toList (growable: false ),
158+ ),
159+ );
160+ }
161+ }
162+
120163enum _DialogType {
121164 AuthorNickname ,
122165 CompanyName ,
0 commit comments