Rework filter on the transactions list screen

Serhii committed Nov 10, 2022 at 13:25 UTC 1f08d8747159a6455a637544714bf69bf9d9eb8b
8 files changed +347 -154
lib/exchange/exchange_provider_description.dart
+5
@@ -24,6 +24,9 @@ class ExchangeProviderDescription extends EnumerableItem<int>
24 static const simpleSwap =
25 ExchangeProviderDescription(title: 'SimpleSwap', raw: 4, image: 'assets/images/simpleSwap.png');
26
27 + static const all =
28 + ExchangeProviderDescription(title: 'All trades', raw: 5, image:'');
29 +
30 static ExchangeProviderDescription deserialize({required int raw}) {
31 switch (raw) {
32 case 0:
@@ -36,6 +39,8 @@ class ExchangeProviderDescription extends EnumerableItem<int>
39 return sideShift;
40 case 4:
41 return simpleSwap;
42 + case 5:
43 + return all;
44 default:
45 throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize');
46 }
lib/src/screens/dashboard/widgets/filter_tile.dart
+1 -6
@@ -9,12 +9,7 @@ class FilterTile extends StatelessWidget {
9 Widget build(BuildContext context) {
10 return Container(
11 width: double.infinity,
12 - padding: EdgeInsets.only(
13 - top: 18,
14 - bottom: 18,
15 - left: 24,
16 - right: 24
17 - ),
12 + padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 24.0),
13 child: child,
14 );
15 }
lib/src/screens/dashboard/widgets/filter_widget.dart
+132 -99
@@ -6,20 +6,21 @@ import 'package:flutter/cupertino.dart';
6 import 'package:flutter/material.dart';
7 import 'package:cake_wallet/src/widgets/alert_background.dart';
8 import 'package:cake_wallet/src/widgets/alert_close_button.dart';
9 -import 'package:cake_wallet/src/widgets/checkbox_widget.dart';
9 +import 'package:cake_wallet/src/widgets/rounded_checkbox.dart';
10 import 'package:cake_wallet/generated/i18n.dart';
11 +import 'package:flutter_mobx/flutter_mobx.dart';
12 //import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
13
14 class FilterWidget extends StatelessWidget {
15 FilterWidget({required this.dashboardViewModel});
16
17 final DashboardViewModel dashboardViewModel;
17 - final backVector = Image.asset('assets/images/back_vector.png',
18 - color: Palette.darkBlueCraiola
19 - );
18 + final closeIcon =
19 + Image.asset('assets/images/close.png', color: Palette.darkBlueCraiola);
20
21 @override
22 Widget build(BuildContext context) {
23 + const sectionDivider = SectionDivider();
24 return AlertBackground(
25 child: Stack(
26 alignment: Alignment.center,
@@ -38,118 +39,150 @@ class FilterWidget extends StatelessWidget {
39 ),
40 ),
41 Padding(
41 - padding: EdgeInsets.only(
42 - left: 24,
43 - right: 24,
44 - top: 24
45 - ),
42 + padding: EdgeInsets.only(left: 24, right: 24, top: 24),
43 child: ClipRRect(
47 - borderRadius: BorderRadius.all(Radius.circular(14)),
44 + borderRadius: BorderRadius.all(Radius.circular(24)),
45 child: Container(
49 - color: Theme.of(context).textTheme!.bodyText1!.decorationColor!,
50 - child: ListView.separated(
51 - shrinkWrap: true,
52 - physics: const NeverScrollableScrollPhysics(),
53 - itemCount: dashboardViewModel.filterItems.length,
54 - separatorBuilder: (context, _) => Container(
55 - height: 1,
56 - color: Theme.of(context).accentTextTheme!.subtitle1!.backgroundColor!,
57 - ),
58 - itemBuilder: (_, index1) {
59 - final title = dashboardViewModel.filterItems.keys.elementAt(index1);
60 - final section = dashboardViewModel.filterItems.values.elementAt(index1);
61 -
62 - return Column(
63 - crossAxisAlignment: CrossAxisAlignment.start,
64 - children: <Widget>[
65 - Padding(
66 - padding: EdgeInsets.only(
67 - top: 20,
68 - left: 24,
69 - right: 24
70 - ),
71 - child: Text(
72 - title,
73 - style: TextStyle(
74 - color: Theme.of(context).accentTextTheme!.subtitle1!.color!,
75 - fontSize: 16,
76 - fontWeight: FontWeight.w500,
77 - fontFamily: 'Lato',
78 - decoration: TextDecoration.none
79 - ),
46 + color: Theme.of(context)
47 + .textTheme!
48 + .bodyText1!
49 + .decorationColor!,
50 + child: Column(
51 + crossAxisAlignment: CrossAxisAlignment.start,
52 + children: [
53 + Padding(
54 + padding: EdgeInsets.all(24.0),
55 + child: Text(
56 + S.of(context).filters,
57 + style: TextStyle(
58 + color: Theme.of(context)
59 + .textTheme!
60 + .bodyText1!
61 + .decorationColor!,
62 + fontSize: 16,
63 + fontFamily: 'Lato',
64 + decoration: TextDecoration.none,
65 ),
66 ),
82 - ListView.separated(
83 - shrinkWrap: true,
84 - physics: const NeverScrollableScrollPhysics(),
85 - itemCount: section.length,
86 - separatorBuilder: (context, _) => Container(
87 - height: 1,
88 - padding: EdgeInsets.only(left: 24),
89 - color: Theme.of(context).textTheme!.bodyText1!.decorationColor!,
90 - child: Container(
91 - height: 1,
92 - color: Theme.of(context).accentTextTheme!.subtitle1!.backgroundColor!,
93 - ),
94 - ),
95 - itemBuilder: (_, index2) {
96 -
97 - final item = section[index2];
98 - final content = item.onChanged != null
99 - ? CheckboxWidget(
100 - value: item.value(),
101 - caption: item.caption,
102 - onChanged: item.onChanged
103 - )
104 - : GestureDetector(
105 - onTap: () async {
106 - //final List<DateTime> picked =
107 - //await date_rage_picker.showDatePicker(
108 - // context: context,
109 - // initialFirstDate: DateTime.now()
110 - // .subtract(Duration(days: 1)),
111 - // initialLastDate: (DateTime.now()),
112 - // firstDate: DateTime(2015),
113 - // lastDate: DateTime.now()
114 - // .add(Duration(days: 1)));
115 -
116 - //if (picked != null && picked.length == 2) {
117 - // dashboardViewModel.transactionFilterStore
118 - // .changeStartDate(picked.first);
119 - // dashboardViewModel.transactionFilterStore
120 - // .changeEndDate(picked.last);
121 - //}
122 - },
123 - child: Padding(
124 - padding: EdgeInsets.only(left: 32),
67 + ),
68 + sectionDivider,
69 + ListView.separated(
70 + padding: EdgeInsets.zero,
71 + shrinkWrap: true,
72 + physics: const NeverScrollableScrollPhysics(),
73 + itemCount: dashboardViewModel.filterItems.length,
74 + separatorBuilder: (context, _) => sectionDivider,
75 + itemBuilder: (_, index1) {
76 + final title = dashboardViewModel.filterItems.keys
77 + .elementAt(index1);
78 + final section = dashboardViewModel
79 + .filterItems.values
80 + .elementAt(index1);
81 + return Column(
82 + crossAxisAlignment: CrossAxisAlignment.start,
83 + children: <Widget>[
84 + Padding(
85 + padding: EdgeInsets.only(
86 + top: 20, left: 24, right: 24),
87 child: Text(
126 - item.caption,
88 + title,
89 style: TextStyle(
128 - color: Theme.of(context).primaryTextTheme!.headline6!.color!,
129 - fontSize: 18,
90 + color: Theme.of(context)
91 + .primaryTextTheme!
92 + .headline6!
93 + .color!,
94 + fontSize: 16,
95 fontFamily: 'Lato',
131 - fontWeight: FontWeight.w500,
132 - decoration: TextDecoration.none
133 - ),
96 + fontWeight: FontWeight.bold,
97 + decoration: TextDecoration.none),
98 ),
99 ),
136 - );
100 + ListView.builder(
101 + padding:
102 + EdgeInsets.symmetric(vertical: 8.0),
103 + shrinkWrap: true,
104 + physics:
105 + const NeverScrollableScrollPhysics(),
106 + itemCount: section.length,
107 + itemBuilder: (_, index2) {
108 + final item = section[index2];
109 + final content = item.onChanged != null
110 + ? Observer(
111 + builder: (_) =>
112 + RoundedCheckboxWidget(
113 + value: item.value.value,
114 + caption: item.caption,
115 + onChanged: item.onChanged,
116 + currentTheme:
117 + dashboardViewModel
118 + .settingsStore
119 + .currentTheme,
120 + ))
121 + : GestureDetector(
122 + onTap: () async {
123 + //final List<DateTime> picked =
124 + //await date_rage_picker.showDatePicker(
125 + // context: context,
126 + // initialFirstDate: DateTime.now()
127 + // .subtract(Duration(days: 1)),
128 + // initialLastDate: (DateTime.now()),
129 + // firstDate: DateTime(2015),
130 + // lastDate: DateTime.now()
131 + // .add(Duration(days: 1)));
132 +
133 + //if (picked != null && picked.length == 2) {
134 + // dashboardViewModel.transactionFilterStore
135 + // .changeStartDate(picked.first);
136 + // dashboardViewModel.transactionFilterStore
137 + // .changeEndDate(picked.last);
138 + //}
139 + },
140 + child: Padding(
141 + padding:
142 + EdgeInsets.only(left: 32),
143 + child: Text(
144 + item.caption,
145 + style: TextStyle(
146 + color: Colors.red,
147 + //Theme.of(context).primaryTextTheme.title.color,//
148 + fontSize: 18,
149 + fontFamily: 'Lato',
150 + fontWeight:
151 + FontWeight.w500,
152 + decoration:
153 + TextDecoration.none),
154 + ),
155 + ),
156 + );
157
138 - return FilterTile(child: content);
139 - },
140 - )
141 - ],
142 - );
143 - },
144 - ),
158 + return FilterTile(child: content);
159 + },
160 + )
161 + ],
162 + );
163 + },
164 + ),
165 + ]),
166 ),
167 ),
168 ),
169 ],
170 ),
150 - AlertCloseButton(image: backVector)
171 + AlertCloseButton(image: closeIcon)
172 ],
173 ),
174 );
175 }
176 +}
177 +
178 +class SectionDivider extends StatelessWidget {
179 + const SectionDivider();
180 +
181 + @override
182 + Widget build(BuildContext context) {
183 + return Container(
184 + height: 1,
185 + color: Colors.red,//Fixme Theme.of(context).accentTextTheme.subhead.backgroundColor,
186 + );
187 + }
188 }
\ No newline at end of file
lib/src/widgets/rounded_checkbox.dart new
+91
@@ -0,0 +1,91 @@
1 +import 'package:cake_wallet/palette.dart';
2 +import 'package:flutter/material.dart';
3 +import 'package:cake_wallet/themes/theme_base.dart';
4 +
5 +class RoundedCheckboxWidget extends StatelessWidget {
6 + RoundedCheckboxWidget(
7 + {required this.value,
8 + required this.caption,
9 + required this.onChanged,
10 + this.currentTheme});
11 +
12 + final bool value;
13 + final String caption;
14 + final Function onChanged;
15 + final ThemeBase? currentTheme;
16 +
17 + bool get darkTheme => currentTheme!.type == ThemeType.dark;
18 +
19 + @override
20 + Widget build(BuildContext context) {
21 +
22 + final baseGradient = LinearGradient(colors: [
23 + Colors.red, //Fixme Theme.of(context).primaryTextTheme!.subtitle!.color!,
24 + Colors.red //Fixme Theme.of(context).primaryTextTheme!.subtitle!.decorationColor!,
25 + ], begin: Alignment.centerLeft, end: Alignment.centerRight);
26 +
27 + final darkThemeGradient = LinearGradient(colors: [
28 + Palette.blueCraiola,
29 + Palette.blueGreyCraiola,
30 + ], begin: Alignment.topLeft, end: Alignment.bottomRight);
31 +
32 + final gradient = darkTheme ? darkThemeGradient : baseGradient;
33 +
34 + final uncheckedColor = darkTheme
35 + ? Colors.red //Fixme Theme.of(context).accentTextTheme.subhead.decorationColor
36 + : Colors.white;
37 +
38 + final borderColor = darkTheme
39 + ? Colors.red //Fixme Theme.of(context).accentTextTheme.subtitle.backgroundColor
40 + : Colors.transparent;
41 +
42 + final checkedOuterBoxDecoration =
43 + BoxDecoration(shape: BoxShape.circle, gradient: gradient);
44 + final outerBoxDecoration = BoxDecoration(
45 + shape: BoxShape.circle,
46 + color: Theme.of(context).accentTextTheme.overline!.color!,
47 + border: Border.all(color: borderColor));
48 +
49 + final checkedInnerBoxDecoration =
50 + BoxDecoration(shape: BoxShape.circle, color: Colors.white);
51 + final innerBoxDecoration =
52 + BoxDecoration(shape: BoxShape.circle, color: uncheckedColor);
53 +
54 + return GestureDetector(
55 + onTap: () => onChanged(),
56 + child: Row(
57 + mainAxisSize: MainAxisSize.min,
58 + mainAxisAlignment: MainAxisAlignment.start,
59 + children: <Widget>[
60 + Container(
61 + height: 24.0,
62 + width: 24.0,
63 + child: DecoratedBox(
64 + decoration:
65 + value ? checkedOuterBoxDecoration : outerBoxDecoration,
66 + child: Padding(
67 + padding: EdgeInsets.all(value ? 4.0 : 1.0),
68 + child: DecoratedBox(
69 + decoration:
70 + value ? checkedInnerBoxDecoration : innerBoxDecoration,
71 + ),
72 + ),
73 + ),
74 + ),
75 + Padding(
76 + padding: EdgeInsets.only(left: 16),
77 + child: Text(
78 + caption,
79 + style: TextStyle(
80 + color: Colors.red, //Fixme Theme.of(context).primaryTextTheme.title.color,
81 + fontSize: 18,
82 + fontFamily: 'Lato',
83 + fontWeight: FontWeight.w500,
84 + decoration: TextDecoration.none),
85 + ),
86 + )
87 + ],
88 + ),
89 + );
90 + }
91 +}
\ No newline at end of file
lib/store/dashboard/trade_filter_store.dart
+51 -27
@@ -8,39 +8,61 @@ part'trade_filter_store.g.dart';
8 class TradeFilterStore = TradeFilterStoreBase with _$TradeFilterStore;
9
10 abstract class TradeFilterStoreBase with Store {
11 - TradeFilterStoreBase(
12 - {this.displayXMRTO = true,
13 - this.displayChangeNow = true,
14 - this.displayMorphToken = true,
15 - this.displaySimpleSwap = true,
16 - });
11 + TradeFilterStoreBase();
12
18 - @observable
19 - bool displayXMRTO;
20 -
21 - @observable
22 - bool displayChangeNow;
23 -
24 - @observable
25 - bool displayMorphToken;
26 -
27 - @observable
28 - bool displaySimpleSwap;
13 + Observable<bool> displayXMRTO = Observable(true);
14 + Observable<bool> displayAllTrades = Observable(true);
15 + Observable<bool> displayChangeNow = Observable(true);
16 + Observable<bool> displaySideShift = Observable(true);
17 + Observable<bool> displayMorphToken = Observable(true);
18 + Observable<bool> displaySimpleSwap = Observable(true);
19
20 @action
21 void toggleDisplayExchange(ExchangeProviderDescription provider) {
22 switch (provider) {
23 case ExchangeProviderDescription.changeNow:
34 - displayChangeNow = !displayChangeNow;
24 + displayAllTrades.value = false;
25 + displayChangeNow.value = !displayChangeNow.value;
26 + if (displayChangeNow.value && displaySideShift.value && displaySimpleSwap.value) {
27 + displayAllTrades.value = true;
28 + }
29 + break;
30 + case ExchangeProviderDescription.sideShift:
31 + displayAllTrades.value = false;
32 + displaySideShift.value = !displaySideShift.value;
33 + if (displayChangeNow.value && displaySideShift.value && displaySimpleSwap.value) {
34 + displayAllTrades.value = true;
35 + }
36 + break;
37 + case ExchangeProviderDescription.simpleSwap:
38 + displayAllTrades.value = false;
39 + displaySimpleSwap.value = !displaySimpleSwap.value;
40 + if (displayChangeNow.value && displaySideShift.value && displaySimpleSwap.value) {
41 + displayAllTrades.value = true;
42 + }
43 break;
44 case ExchangeProviderDescription.xmrto:
37 - displayXMRTO = !displayXMRTO;
45 + displayXMRTO.value = !displayXMRTO.value;
46 break;
47 case ExchangeProviderDescription.morphToken:
40 - displayMorphToken = !displayMorphToken;
48 + displayMorphToken.value = !displayMorphToken.value;
49 break;
42 - case ExchangeProviderDescription.simpleSwap:
43 - displaySimpleSwap = !displaySimpleSwap;
50 + case ExchangeProviderDescription.all:
51 + displayAllTrades.value = !displayAllTrades.value;
52 + if (displayAllTrades.value) {
53 + displayChangeNow.value = true;
54 + displaySideShift.value = true;
55 + displayXMRTO.value = true;
56 + displayMorphToken.value = true;
57 + displaySimpleSwap.value = true;
58 + }
59 + if (!displayAllTrades.value) {
60 + displayChangeNow.value = false;
61 + displaySideShift.value = false;
62 + displayXMRTO.value = false;
63 + displayMorphToken.value = false;
64 + displaySimpleSwap.value = false;
65 + }
66 break;
67 }
68 }
@@ -48,20 +70,22 @@ abstract class TradeFilterStoreBase with Store {
70 List<TradeListItem> filtered({required List<TradeListItem> trades, required WalletBase wallet}) {
71 final _trades =
72 trades.where((item) => item.trade.walletId == wallet.id).toList();
51 - final needToFilter = !displayChangeNow || !displayXMRTO || !displayMorphToken || !displaySimpleSwap;
73 + final needToFilter = !displayChangeNow.value || !displaySideShift.value
74 + || !displayXMRTO.value || !displayMorphToken.value
75 + || !displaySimpleSwap.value;
76
77 return needToFilter
78 ? _trades
79 .where((item) =>
56 - (displayXMRTO &&
80 + (displayXMRTO.value &&
81 item.trade.provider == ExchangeProviderDescription.xmrto) ||
58 - (displayChangeNow &&
82 + (displayChangeNow.value &&
83 item.trade.provider ==
84 ExchangeProviderDescription.changeNow) ||
61 - (displayMorphToken &&
85 + (displayMorphToken.value &&
86 item.trade.provider ==
87 ExchangeProviderDescription.morphToken)
64 - ||(displaySimpleSwap &&
88 + ||(displaySimpleSwap.value &&
89 item.trade.provider ==
90 ExchangeProviderDescription.simpleSwap))
91 .toList()
lib/store/dashboard/transaction_filter_store.dart
+43 -14
@@ -1,6 +1,8 @@
1 import 'package:mobx/mobx.dart';
2 import 'package:cw_core/transaction_direction.dart';
3 import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
4 +import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
5 +import 'package:cake_wallet/generated/i18n.dart';
6
7 part 'transaction_filter_store.g.dart';
8
@@ -8,14 +10,11 @@ class TransactionFilterStore = TransactionFilterStoreBase
10 with _$TransactionFilterStore;
11
12 abstract class TransactionFilterStoreBase with Store {
11 - TransactionFilterStoreBase(
12 - {this.displayIncoming = true, this.displayOutgoing = true});
13 + TransactionFilterStoreBase();
14
14 - @observable
15 - bool displayIncoming;
16 -
17 - @observable
18 - bool displayOutgoing;
15 + Observable<bool> displayAll = Observable(true);
16 + Observable<bool> displayIncoming = Observable(true);
17 + Observable<bool> displayOutgoing = Observable(true);
18
19 @observable
20 DateTime? startDate;
@@ -24,10 +23,40 @@ abstract class TransactionFilterStoreBase with Store {
23 DateTime? endDate;
24
25 @action
27 - void toggleIncoming() => displayIncoming = !displayIncoming;
26 + void toggleIAll() {
27 + displayAll.value = (!displayAll.value);
28 + if (displayAll.value) {
29 + displayOutgoing.value = true;
30 + displayIncoming.value = true;
31 + }
32 + if (!displayAll.value) {
33 + displayOutgoing.value = false;
34 + displayIncoming.value = false;
35 + }
36 + }
37
38 @action
30 - void toggleOutgoing() => displayOutgoing = !displayOutgoing;
39 + void toggleIncoming() {
40 + displayIncoming.value = (!displayIncoming.value);
41 + if (displayIncoming.value && displayOutgoing.value) {
42 + displayAll.value = true;
43 + }
44 + if (!displayIncoming.value || !displayOutgoing.value) {
45 + displayAll.value = false;
46 + }
47 + }
48 +
49 +
50 + @action
51 + void toggleOutgoing() {
52 + displayOutgoing.value = (!displayOutgoing.value);
53 + if (displayIncoming.value && displayOutgoing.value) {
54 + displayAll.value = true;
55 + }
56 + if (!displayIncoming.value || !displayOutgoing.value) {
57 + displayAll.value = false;
58 + }
59 + }
60
61 @action
62 void changeStartDate(DateTime date) => startDate = date;
@@ -37,8 +66,8 @@ abstract class TransactionFilterStoreBase with Store {
66
67 List<TransactionListItem> filtered({required List<TransactionListItem> transactions}) {
68 var _transactions = <TransactionListItem>[];
40 - final needToFilter = !displayOutgoing ||
41 - !displayIncoming ||
69 + final needToFilter = !displayOutgoing.value ||
70 + !displayIncoming.value ||
71 (startDate != null && endDate != null);
72
73 if (needToFilter) {
@@ -50,11 +79,11 @@ abstract class TransactionFilterStoreBase with Store {
79 && (endDate?.isAfter(item.transaction.date) ?? false);
80 }
81
53 - if (allowed && (!displayOutgoing || !displayIncoming)) {
54 - allowed = (displayOutgoing &&
82 + if (allowed && (!displayOutgoing.value || !displayIncoming.value)) {
83 + allowed = (displayOutgoing.value &&
84 item.transaction.direction ==
85 TransactionDirection.outgoing) ||
57 - (displayIncoming &&
86 + (displayIncoming.value &&
87 item.transaction.direction == TransactionDirection.incoming);
88 }
89
lib/view_model/dashboard/dashboard_view_model.dart
+20 -6
@@ -60,13 +60,17 @@ abstract class DashboardViewModelBase with Store {
60 filterItems = {
61 S.current.transactions: [
62 FilterItem(
63 - value: () => transactionFilterStore.displayIncoming,
63 + value: transactionFilterStore.displayAll,
64 + caption: 'S.current.all_transactions',//Fixme
65 + onChanged: () => transactionFilterStore.toggleIAll()),
66 + FilterItem(
67 + value: transactionFilterStore.displayIncoming,
68 caption: S.current.incoming,
65 - onChanged: (value) => transactionFilterStore.toggleIncoming()),
69 + onChanged: () => transactionFilterStore.toggleIncoming()),
70 FilterItem(
67 - value: () => transactionFilterStore.displayOutgoing,
71 + value: transactionFilterStore.displayOutgoing,
72 caption: S.current.outgoing,
69 - onChanged: (value) => transactionFilterStore.toggleOutgoing()),
73 + onChanged: () => transactionFilterStore.toggleOutgoing()),
74 // FilterItem(
75 // value: () => false,
76 // caption: S.current.transactions_by_date,
@@ -74,10 +78,20 @@ abstract class DashboardViewModelBase with Store {
78 ],
79 S.current.trades: [
80 FilterItem(
77 - value: () => tradeFilterStore.displayChangeNow,
81 + value: tradeFilterStore.displayAllTrades,
82 + caption: 'S.current.all_trades',//Fixme
83 + onChanged: () => tradeFilterStore
84 + .toggleDisplayExchange(ExchangeProviderDescription.all)),
85 + FilterItem(
86 + value: tradeFilterStore.displayChangeNow,
87 caption: 'Change.NOW',
79 - onChanged: (value) => tradeFilterStore
88 + onChanged: () => tradeFilterStore
89 .toggleDisplayExchange(ExchangeProviderDescription.changeNow)),
90 + FilterItem(
91 + value: tradeFilterStore.displaySideShift,
92 + caption: 'SideShift',
93 + onChanged: () => tradeFilterStore
94 + .toggleDisplayExchange(ExchangeProviderDescription.sideShift)),
95 ]
96 },
97 subname = '',
lib/view_model/dashboard/filter_item.dart
+4 -2
@@ -1,10 +1,12 @@
1 +import 'package:mobx/mobx.dart';
2 +
3 class FilterItem {
4 FilterItem({
5 required this.value,
6 required this.caption,
7 required this.onChanged});
8
7 - bool Function() value;
9 + Observable<bool> value;
10 String caption;
9 - Function(bool) onChanged;
11 + Function onChanged;
12 }
\ No newline at end of file