What changed, and why it matters
This commit is a routine user-interface cleanup: it moves the date-range picker code out of the transaction-search screen and into a reusable widget file. There is no security-relevant change visible in the diff.
No security action required; treat as normal refactoring review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change extracts an inline _buildDateRangePicker() method from transaction_search_filter_view.dart into a new StackDateRangePicker StatelessWidget in lib/widgets/date_picker/date_picker.dart. It also updates showSWDatePicker to accept a value list instead of a single currentDate and defaults that list to empty. The logic for selecting from/to dates, enforcing that from <= to, and the underlying showSWDatePicker implementation remain functionally unchanged. No cryptographic, networking, storage, or permission code is touched.
Changed components
lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dartlib/widgets/date_picker/date_picker.dartInspect captured patch +239 / −234
diff --git a/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart b/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart
index 453d908..b799d10 100644
--- a/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart
+++ b/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart
@@ -11,7 +11,6 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:flutter_svg/svg.dart';
import '../../../models/transaction_filter.dart';
import '../../../providers/global/locale_provider.dart';
@@ -21,9 +20,7 @@ import '../../../themes/theme_providers.dart';
import '../../../utilities/amount/amount.dart';
import '../../../utilities/amount/amount_formatter.dart';
import '../../../utilities/amount/amount_input_formatter.dart';
-import '../../../utilities/assets.dart';
import '../../../utilities/constants.dart';
-import '../../../utilities/format.dart';
import '../../../utilities/text_styles.dart';
import '../../../utilities/util.dart';
import '../../../wallets/crypto_currency/crypto_currency.dart';
@@ -60,9 +57,6 @@ class _TransactionSearchViewState
bool _isActiveSentCheckbox = false;
bool _isActiveTradeCheckbox = false;
- String _fromDateString = "";
- String _toDateString = "";
-
final keywordTextFieldFocusNode = FocusNode();
final amountTextFieldFocusNode = FocusNode();
@@ -79,13 +73,6 @@ class _TransactionSearchViewState
_selectedFromDate = filterState.from;
_keywordTextEditingController.text = filterState.keyword;
- _fromDateString = _selectedFromDate == null
- ? ""
- : Format.formatDate(_selectedFromDate!);
- _toDateString = _selectedToDate == null
- ? ""
- : Format.formatDate(_selectedToDate!);
-
final String amount = filterState.amount == null
? ""
: ref
@@ -108,226 +95,9 @@ class _TransactionSearchViewState
super.dispose();
}
- // The following two getters are not required if the
- // date fields are to remain unclearable.
- Widget get _dateFromText {
- final isDateSelected = _fromDateString.isEmpty;
- return Text(
- isDateSelected ? "From..." : _fromDateString,
- style: STextStyles.fieldLabel(context).copyWith(
- color: isDateSelected
- ? Theme.of(context).extension<StackColors>()!.textSubtitle2
- : Theme.of(context).extension<StackColors>()!.accentColorDark,
- ),
- );
- }
-
- Widget get _dateToText {
- final isDateSelected = _toDateString.isEmpty;
- return Text(
- isDateSelected ? "To..." : _toDateString,
- style: STextStyles.fieldLabel(context).copyWith(
- color: isDateSelected
- ? Theme.of(context).extension<StackColors>()!.textSubtitle2
- : Theme.of(context).extension<StackColors>()!.accentColorDark,
- ),
- );
- }
-
DateTime? _selectedFromDate = DateTime(2007);
DateTime? _selectedToDate = DateTime.now();
- Widget _buildDateRangePicker() {
- const middleSeparatorPadding = 2.0;
- const middleSeparatorWidth = 12.0;
- final isDesktop = Util.isDesktop;
-
- final width = isDesktop
- ? null
- : (MediaQuery.of(context).size.width -
- (middleSeparatorWidth +
- (2 * middleSeparatorPadding) +
- (2 * Constants.size.standardPadding))) /
- 2;
-
- return Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Expanded(
- child: GestureDetector(
- key: const Key("transactionSearchViewFromDatePickerKey"),
- onTap: () async {
- // check and hide keyboard
- if (FocusScope.of(context).hasFocus) {
- FocusScope.of(context).unfocus();
- await Future<void>.delayed(const Duration(milliseconds: 125));
- }
-
- if (mounted) {
- final date = (await showSWDatePicker(context))?.first;
- if (date != null) {
- _selectedFromDate = date;
-
- // flag to adjust date so from date is always before to date
- final flag =
- _selectedToDate != null &&
- !_selectedFromDate!.isBefore(_selectedToDate!);
- if (flag) {
- _selectedToDate = DateTime.fromMillisecondsSinceEpoch(
- _selectedFromDate!.millisecondsSinceEpoch,
- );
- }
-
- setState(() {
- if (flag) {
- _toDateString = _selectedToDate == null
- ? ""
- : Format.formatDate(_selectedToDate!);
- }
- _fromDateString = _selectedFromDate == null
- ? ""
- : Format.formatDate(_selectedFromDate!);
- });
- }
- }
- },
- child: Container(
- width: width,
- decoration: BoxDecoration(
- color: Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- border: Border.all(
- color: Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
- width: 1,
- ),
- ),
- child: Padding(
- padding: EdgeInsets.symmetric(
- horizontal: 12,
- vertical: isDesktop ? 17 : 12,
- ),
- child: Row(
- children: [
- SvgPicture.asset(
- Assets.svg.calendar,
- height: 20,
- width: 20,
- color: Theme.of(
- context,
- ).extension<StackColors>()!.textSubtitle2,
- ),
- const SizedBox(width: 10),
- Align(
- alignment: Alignment.centerLeft,
- child: FittedBox(child: _dateFromText),
- ),
- ],
- ),
- ),
- ),
- ),
- ),
- Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: middleSeparatorPadding,
- ),
- child: Container(
- width: middleSeparatorWidth,
- // height: 1,
- // color: CFColors.smoke,
- ),
- ),
- Expanded(
- child: GestureDetector(
- key: const Key("transactionSearchViewToDatePickerKey"),
- onTap: () async {
- // check and hide keyboard
- if (FocusScope.of(context).hasFocus) {
- FocusScope.of(context).unfocus();
- await Future<void>.delayed(const Duration(milliseconds: 125));
- }
-
- if (mounted) {
- final date = (await showSWDatePicker(context))?.first;
- if (date != null) {
- _selectedToDate = date;
-
- // flag to adjust date so from date is always before to date
- final flag =
- _selectedFromDate != null &&
- !_selectedToDate!.isAfter(_selectedFromDate!);
- if (flag) {
- _selectedFromDate = DateTime.fromMillisecondsSinceEpoch(
- _selectedToDate!.millisecondsSinceEpoch,
- );
- }
-
- setState(() {
- if (flag) {
- _fromDateString = _selectedFromDate == null
- ? ""
- : Format.formatDate(_selectedFromDate!);
- }
- _toDateString = _selectedToDate == null
- ? ""
- : Format.formatDate(_selectedToDate!);
- });
- }
- }
- },
- child: Container(
- width: width,
- decoration: BoxDecoration(
- color: Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- border: Border.all(
- color: Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
- width: 1,
- ),
- ),
- child: Padding(
- padding: EdgeInsets.symmetric(
- horizontal: 12,
- vertical: isDesktop ? 17 : 12,
- ),
- child: Row(
- children: [
- SvgPicture.asset(
- Assets.svg.calendar,
- height: 20,
- width: 20,
- color: Theme.of(
- context,
- ).extension<StackColors>()!.textSubtitle2,
- ),
- const SizedBox(width: 10),
- Align(
- alignment: Alignment.centerLeft,
- child: FittedBox(child: _dateToText),
- ),
- ],
- ),
- ),
- ),
- ),
- ),
- if (isDesktop) const SizedBox(width: 24),
- ],
- );
- }
-
@override
Widget build(BuildContext context) {
if (Util.isDesktop) {
@@ -597,7 +367,19 @@ class _TransactionSearchViewState
),
),
SizedBox(height: isDesktop ? 10 : 8),
- _buildDateRangePicker(),
+ Padding(
+ padding: isDesktop ? const .only(right: 32) : .zero,
+ child: StackDateRangePicker(
+ fromDate: _selectedFromDate,
+ toDate: _selectedToDate,
+ onChanged: (from, to) {
+ setState(() {
+ _selectedFromDate = from;
+ _selectedToDate = to;
+ });
+ },
+ ),
+ ),
SizedBox(height: isDesktop ? 32 : 24),
Align(
alignment: Alignment.centerLeft,
diff --git a/lib/widgets/date_picker/date_picker.dart b/lib/widgets/date_picker/date_picker.dart
index 4019b66..9655fe2 100644
--- a/lib/widgets/date_picker/date_picker.dart
+++ b/lib/widgets/date_picker/date_picker.dart
@@ -2,9 +2,13 @@ import 'dart:math';
import 'package:calendar_date_picker2/calendar_date_picker2.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
import '../../themes/stack_colors.dart';
+import '../../utilities/assets.dart';
import '../../utilities/constants.dart';
+import '../../utilities/format.dart';
+import '../../utilities/text_styles.dart';
import '../../utilities/util.dart';
import '../conditional_parent.dart';
import '../desktop/primary_button.dart';
@@ -12,11 +16,13 @@ import '../desktop/secondary_button.dart';
part 'sw_date_picker.dart';
+/// [value] holds selected dates. One if [range] is false. Start and end dates
+/// otherwise.
Future<List<DateTime?>?> showSWDatePicker(
BuildContext context, {
DateTime? firstDate,
DateTime? lastDate,
- DateTime? currentDate,
+ List<DateTime?> value = const [],
bool range = false,
}) async {
final Size size;
@@ -34,12 +40,12 @@ Future<List<DateTime?>?> showSWDatePicker(
final dates = await _showDatePickerDialog(
context: context,
- value: [now],
+ value: value,
dialogSize: size,
config: CalendarDatePicker2WithActionButtonsConfig(
firstDate: firstDate ?? DateTime(2007),
lastDate: lastDate ?? now,
- currentDate: currentDate ?? now,
+ currentDate: now,
rangeBidirectional: range ? false : null,
calendarType: range ? .range : null,
buttonPadding: const EdgeInsets.only(right: 16),
@@ -109,3 +115,220 @@ Future<List<DateTime?>?> _showDatePickerDialog({
useSafeArea: useSafeArea,
);
}
+
+class StackDateRangePicker extends StatelessWidget {
+ const StackDateRangePicker({
+ super.key,
+ required this.fromDate,
+ required this.toDate,
+ this.firstDate,
+ this.lastDate,
+ required this.onChanged,
+ });
+
+ final DateTime? fromDate;
+ final DateTime? toDate;
+ final DateTime? firstDate, lastDate;
+ final void Function(DateTime? from, DateTime? to) onChanged;
+
+ @override
+ Widget build(BuildContext context) {
+ const middleSeparatorPadding = 2.0;
+ const middleSeparatorWidth = 12.0;
+ final isDesktop = Util.isDesktop;
+
+ final String fromDateString = switch (fromDate) {
+ null => "",
+ final d => Format.formatDate(d),
+ };
+ final String toDateString = switch (toDate) {
+ null => "",
+ final d => Format.formatDate(d),
+ };
+
+ return Row(
+ children: [
+ Expanded(
+ child: MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: GestureDetector(
+ key: const Key("transactionSearchViewFromDatePickerKey"),
+ onTap: () async {
+ // check and hide keyboard
+ if (FocusScope.of(context).hasFocus) {
+ FocusScope.of(context).unfocus();
+ await Future<void>.delayed(const Duration(milliseconds: 125));
+ }
+
+ if (context.mounted) {
+ final date = (await showSWDatePicker(
+ context,
+ firstDate: firstDate,
+ lastDate: lastDate,
+ ))?.first;
+ if (date != null) {
+ final newFrom = date;
+ DateTime? newTo = toDate;
+
+ // flag to adjust date so from date is always before to date
+ if (newTo != null && !newFrom.isBefore(newTo)) {
+ newTo = DateTime.fromMillisecondsSinceEpoch(
+ newFrom.millisecondsSinceEpoch,
+ );
+ }
+
+ onChanged(newFrom, newTo);
+ }
+ }
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ border: Border.all(
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
+ width: 1,
+ ),
+ ),
+ child: Padding(
+ padding: EdgeInsets.symmetric(
+ horizontal: 12,
+ vertical: isDesktop ? 17 : 12,
+ ),
+ child: Row(
+ children: [
+ SvgPicture.asset(
+ Assets.svg.calendar,
+ height: 20,
+ width: 20,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textSubtitle2,
+ ),
+ const SizedBox(width: 10),
+ Expanded(
+ child: Text(
+ fromDateString.isEmpty ? "From..." : fromDateString,
+ style: STextStyles.fieldLabel(context).copyWith(
+ color: fromDateString.isEmpty
+ ? Theme.of(
+ context,
+ ).extension<StackColors>()!.textSubtitle2
+ : Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: middleSeparatorPadding,
+ ),
+ child: Container(
+ width: middleSeparatorWidth,
+ // height: 1,
+ // color: CFColors.smoke,
+ ),
+ ),
+ Expanded(
+ child: MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: GestureDetector(
+ key: const Key("transactionSearchViewToDatePickerKey"),
+ onTap: () async {
+ // check and hide keyboard
+ if (FocusScope.of(context).hasFocus) {
+ FocusScope.of(context).unfocus();
+ await Future<void>.delayed(const Duration(milliseconds: 125));
+ }
+
+ if (context.mounted) {
+ final date = (await showSWDatePicker(
+ context,
+ firstDate: firstDate,
+ lastDate: lastDate,
+ ))?.first;
+ if (date != null) {
+ final newTo = date;
+ DateTime? newFrom = fromDate;
+
+ // flag to adjust date so from date is always before to date
+ if (newFrom != null && !newTo.isAfter(newFrom)) {
+ newFrom = DateTime.fromMillisecondsSinceEpoch(
+ newTo.millisecondsSinceEpoch,
+ );
+ }
+
+ onChanged(newFrom, newTo);
+ }
+ }
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ border: Border.all(
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
+ width: 1,
+ ),
+ ),
+ child: Padding(
+ padding: EdgeInsets.symmetric(
+ horizontal: 12,
+ vertical: isDesktop ? 17 : 12,
+ ),
+ child: Row(
+ children: [
+ SvgPicture.asset(
+ Assets.svg.calendar,
+ height: 20,
+ width: 20,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textSubtitle2,
+ ),
+ const SizedBox(width: 10),
+ Expanded(
+ child: Text(
+ toDateString.isEmpty ? "To..." : toDateString,
+ style: STextStyles.fieldLabel(context).copyWith(
+ color: toDateString.isEmpty
+ ? Theme.of(
+ context,
+ ).extension<StackColors>()!.textSubtitle2
+ : Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.