Cw 1038 filter transaction popup not scrollable (#2207)

* ui:make overflowing filter sections scrollable

* Update pull_request_template.md
This commit is contained in:
Serhii 2025-04-18 15:53:22 +03:00 committed by GitHub
parent 66e1745ad9
commit ffe1c115fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 164 additions and 112 deletions

View file

@ -11,3 +11,4 @@ Please include a summary of the changes and which issue is fixed / feature is ad
- [ ] Format code
- [ ] Look for code duplication
- [ ] Clear naming for variables and methods
- [ ] Manual tests in accessibility mode (TalkBack on Android) passed

View file

@ -1,123 +1,178 @@
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/filter_tile.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:cake_wallet/themes/extensions/menu_theme.dart';
import 'package:cake_wallet/view_model/dashboard/dropdown_filter_item.dart';
import 'package:cake_wallet/view_model/dashboard/dropdown_filter_item_widget.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
//import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
class FilterWidget extends StatelessWidget {
FilterWidget({required this.filterItems});
class FilterWidget extends StatefulWidget {
const FilterWidget({required this.filterItems, this.onClose, Key? key}) : super(key: key);
final Map<String, List<FilterItem>> filterItems;
final Function()? onClose;
@override
_FilterWidgetState createState() => _FilterWidgetState();
}
class _FilterWidgetState extends State<FilterWidget> {
final ScrollController _scrollController = ScrollController();
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
const sectionDivider = const HorizontalSectionDivider();
return PickerWrapperWidget(
children: [
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(24)),
child: Container(
color: Theme.of(context).extension<CakeMenuTheme>()!.backgroundColor,
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Padding(
padding: EdgeInsets.all(24.0),
child: Text(
S.of(context).filter_by,
style: TextStyle(
color:
Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
fontSize: 16,
fontFamily: 'Lato',
decoration: TextDecoration.none,
),
),
),
sectionDivider,
ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: filterItems.length,
separatorBuilder: (context, _) => sectionDivider,
itemBuilder: (_, index1) {
final title = filterItems.keys.elementAt(index1);
final section = filterItems.values.elementAt(index1);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 20, left: 24, right: 24),
child: Text(
title,
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none),
),
return AlertBackground(
child: Column(
children: [
const Expanded(child: SizedBox()),
Expanded(
flex: responsiveLayoutUtil.shouldRenderTabletUI ? 16 : 8,
child: LayoutBuilder(
builder: (context, constraints) {
double availableHeight = constraints.maxHeight;
return _buildFilterContent(context, availableHeight);
},
),
),
Expanded(
child: AlertCloseButton(
key: const ValueKey('filter_wrapper_close_button_key'),
isPositioned: false,
onTap: widget.onClose,
),
),
const SizedBox(height: 24),
],
),
);
}
Widget _buildFilterContent(BuildContext context, double availableHeight) {
const sectionDivider = HorizontalSectionDivider();
const double totalHeaderHeight = 73;
const double filterTileMinHeight = 40;
double availableHeightForItems = availableHeight - totalHeaderHeight;
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(left: 24, right: 24, top: 24),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Container(
color: Theme.of(context).extension<CakeMenuTheme>()!.backgroundColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(24.0),
child: Text(
S.of(context).filter_by,
style: TextStyle(
color: Theme.of(context)
.extension<TransactionTradeTheme>()!
.detailsTitlesColor,
fontSize: 16,
fontFamily: 'Lato',
decoration: TextDecoration.none,
),
ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 28.0),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
),
),
sectionDivider,
ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: widget.filterItems.length,
separatorBuilder: (context, _) => sectionDivider,
itemBuilder: (_, index1) {
final title = widget.filterItems.keys.elementAt(index1);
final section = widget.filterItems.values.elementAt(index1);
final double itemHeight =
availableHeightForItems / widget.filterItems.length;
final isSectionScrollable =
(itemHeight < (section.length * filterTileMinHeight));
final Widget sectionListView = ListView.builder(
controller: isSectionScrollable ? _scrollController : null,
padding: const EdgeInsets.symmetric(horizontal: 28.0),
shrinkWrap: isSectionScrollable ? false : true,
physics: isSectionScrollable
? const BouncingScrollPhysics()
: const NeverScrollableScrollPhysics(),
itemCount: section.length,
itemBuilder: (_, index2) {
final item = section[index2];
if (item is DropdownFilterItem) {
return Padding(
padding: EdgeInsets.fromLTRB(8, 0, 8, 16),
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor),
),
),
child: DropdownFilterList(
items: item.items,
caption: item.caption,
selectedItem: item.selectedItem,
onItemSelected: item.onItemSelected,
),
),
);
}
final content = Observer(
builder: (_) => StandardCheckbox(
value: item.value(),
caption: item.caption,
gradientBackground: true,
borderColor: Theme.of(context).dividerColor,
iconColor: Colors.white,
onChanged: (value) => item.onChanged(),
));
return FilterTile(child: content);
builder: (_) => StandardCheckbox(
value: item.value(),
caption: item.caption,
gradientBackground: true,
borderColor: Theme.of(context).dividerColor,
iconColor: Colors.white,
onChanged: (value) => item.onChanged(),
),
);
return FilterTile(
child: content,
);
},
)
],
);
},
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 20, left: 24, right: 24),
child: Text(
title,
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
),
),
),
Container(
height: isSectionScrollable ? itemHeight - totalHeaderHeight : null,
child: isSectionScrollable
? Scrollbar(
controller: _scrollController,
thumbVisibility: true,
child: sectionListView,
)
: sectionListView,
),
],
);
},
),
],
),
]),
),
),
),
)
],
],
),
);
}
}

View file

@ -7,6 +7,7 @@ class AlertCloseButton extends StatelessWidget {
this.image,
this.bottom,
this.onTap,
this.isPositioned = true,
super.key,
});
@ -14,6 +15,7 @@ class AlertCloseButton extends StatelessWidget {
final Image? image;
final double? bottom;
final bool isPositioned;
final closeButton = Image.asset(
'assets/images/close.png',
@ -22,24 +24,18 @@ class AlertCloseButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Positioned(
bottom: bottom ?? 60,
child: GestureDetector(
final button = GestureDetector(
onTap: onTap ?? () => Navigator.of(context).pop(),
child: Semantics(
label: S.of(context).close,
button: true,
enabled: true,
child: Container(
height: 42,
width: 42,
decoration: BoxDecoration(color: Colors.white, shape: BoxShape.circle),
child: Center(
child: image ?? closeButton,
),
),
),
),
);
label: S.of(context).close,
button: true,
enabled: true,
child: Container(
height: 42,
width: 42,
decoration: BoxDecoration(color: Colors.white, shape: BoxShape.circle),
child: Center(child: image ?? closeButton))));
return isPositioned ? Positioned(bottom: bottom ?? 60, child: button) : button;
}
}