LCOV - code coverage report
Current view: top level - lib/design/form - list_selector.dart Coverage Total Hit
Test: lcov.info Lines: 65.7 % 67 44
Test Date: 2026-11-14 15:04:37 Functions: - 0 0

            Line data    Source code
       1              : // Copyright 2023 The terCAD team. All rights reserved.
       2              : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
       3              : 
       4              : import 'package:app_finance/_classes/controller/focus_controller.dart';
       5              : import 'package:app_finance/_classes/herald/app_design.dart';
       6              : import 'package:app_finance/design/form/list_selector_item.dart';
       7              : import 'package:app_finance/_configs/custom_color_scheme.dart';
       8              : import 'package:app_finance/_configs/custom_text_theme.dart';
       9              : import 'package:app_finance/_configs/theme_helper.dart';
      10              : import 'package:app_finance/_ext/build_context_ext.dart';
      11              : import 'package:app_finance/design/form/list_selector_page.dart';
      12              : import 'package:app_finance/design/wrapper/focus_wrapper.dart';
      13              : import 'package:app_finance/design/wrapper/row_widget.dart';
      14              : import 'package:app_finance/design/wrapper/tap_widget.dart';
      15              : import 'package:app_finance/design/wrapper/text_wrapper.dart';
      16              : import 'package:material_ui/material_ui.dart';
      17              : 
      18              : typedef FnState = Function(ListSelectorItem? value);
      19              : 
      20              : class ListSelector<K extends ListSelectorItem> extends StatefulWidget {
      21              :   final List<K> options;
      22              :   final K? value;
      23              :   final FnState setState;
      24              :   final String? hintText;
      25              :   final String? tooltip;
      26              :   final TextStyle? hintStyle;
      27              :   final Color? hintColor;
      28              :   final bool withLabel;
      29              :   final bool isDisabled;
      30              : 
      31            4 :   const ListSelector({
      32              :     super.key,
      33              :     required this.options,
      34              :     required this.setState,
      35              :     this.hintText,
      36              :     this.tooltip,
      37              :     this.hintStyle,
      38              :     this.hintColor,
      39              :     this.value,
      40              :     this.withLabel = false,
      41              :     this.isDisabled = false,
      42              :   });
      43              : 
      44            4 :   @override
      45            4 :   ListSelectorState createState() => ListSelectorState();
      46              : }
      47              : 
      48              : class ListSelectorState<T extends ListSelector, K extends ListSelectorItem> extends State<T> {
      49              :   final textController = SearchController();
      50              :   late FocusController focusController;
      51            0 :   FntSelectorCallback? getItemBuilder() => null;
      52              : 
      53            0 :   void onTap(BuildContext context) async {
      54            0 :     focusController.blur();
      55              :     FocusController.force = true;
      56            0 :     final result = await Navigator.push(
      57              :       context,
      58            0 :       MaterialPageRoute(
      59            0 :         builder: (context) => ListSelectorPage(
      60            0 :           options: widget.options,
      61            0 :           result: widget.value,
      62            0 :           tooltip: widget.tooltip ?? widget.hintText ?? '...',
      63            0 :           itemBuilder: getItemBuilder(),
      64              :         ),
      65              :       ),
      66              :     );
      67              :     FocusController.force = false;
      68            0 :     widget.setState(result);
      69            0 :     WidgetsBinding.instance.addPostFrameCallback((_) {
      70            0 :       focusController.onEditingComplete(this);
      71            0 :       FocusManager.instance.primaryFocus?.unfocus();
      72              :     });
      73              :   }
      74              : 
      75            4 :   @override
      76              :   Widget build(BuildContext context) {
      77           10 :     focusController = FocusWrapper.of(context) ?? FocusController();
      78            4 :     final indent = ThemeHelper.getIndent(1.5);
      79           12 :     final hintStyle = context.textTheme.tooltipMedium.copyWith(overflow: TextOverflow.ellipsis);
      80            8 :     final labelStyle = context.textTheme.tooltipSmall;
      81           48 :     K? item = widget.value != null ? widget.options.cast().where((e) => e.equal(widget.value?.id)).firstOrNull : null;
      82              : 
      83            4 :     return InkWell(
      84            8 :       autofocus: focusController.isFocused(this),
      85           16 :       focusNode: focusController.bind(this, context: context, value: widget.value),
      86            4 :       child: TapWidget(
      87            8 :         tooltip: widget.tooltip ?? '',
      88            0 :         onTap: () => widget.isDisabled ? null : onTap(context),
      89            0 :         onFocusChange: (v) => v ? focusController.scrollToFocusedElement(this) : null,
      90            4 :         child: Container(
      91              :           width: double.infinity,
      92            8 :           color: widget.isDisabled
      93            3 :               ? context.colorScheme.onSurface.withValues(alpha: 0.1)
      94            8 :               : context.colorScheme.fieldBackground,
      95            8 :           child: LayoutBuilder(builder: (_, constraints) {
      96            4 :             return RowWidget(
      97           12 :               chunk: [null, ThemeHelper.barHeight - ThemeHelper.getIndent()],
      98            4 :               maxWidth: constraints.maxWidth,
      99              :               indent: 0.0,
     100            4 :               children: [
     101            4 :                 [
     102            4 :                   Container(
     103            4 :                     padding: EdgeInsets.fromLTRB(
     104            4 :                       indent / 1.5,
     105            8 :                       widget.withLabel && item != null ? 1 : indent,
     106            4 :                       AppDesign.isRightToLeft() ? ThemeHelper.getIndent() : 0,
     107            8 :                       widget.withLabel && item != null ? indent / 2 : indent,
     108              :                     ),
     109           12 :                     constraints: BoxConstraints(maxWidth: constraints.maxWidth - ThemeHelper.barHeight),
     110              :                     child: item != null
     111            8 :                         ? widget.withLabel
     112            0 :                             ? Column(
     113            0 :                                 mainAxisAlignment: AppDesign.getAlignment<MainAxisAlignment>(),
     114            0 :                                 crossAxisAlignment: AppDesign.getAlignment(),
     115            0 :                                 children: [
     116            0 :                                   TextWrapper(widget.hintText ?? '...', style: labelStyle),
     117            0 :                                   item.build(context),
     118              :                                 ],
     119              :                               )
     120            4 :                             : item.build(context)
     121            4 :                         : TextWrapper(
     122            8 :                             widget.hintText ?? '...',
     123            8 :                             style: widget.hintStyle ?? hintStyle,
     124              :                           ),
     125              :                   ),
     126              :                 ],
     127            4 :                 [
     128            4 :                   Padding(
     129            8 :                     padding: EdgeInsets.only(top: indent / 3),
     130            4 :                     child: ExcludeFocus(
     131            8 :                       child: widget.isDisabled
     132              :                           ? ThemeHelper.emptyBox
     133            4 :                           : IconButton(
     134            8 :                               tooltip: widget.hintText ?? '...',
     135           12 :                               icon: Icon(Icons.arrow_drop_down, color: widget.hintStyle?.color),
     136            0 :                               onPressed: () => onTap(context),
     137              :                             ),
     138              :                     ),
     139              :                   ),
     140              :                 ],
     141              :               ],
     142              :             );
     143              :           }),
     144              :         ),
     145              :       ),
     146              :     );
     147              :   }
     148              : }
        

Generated by: LCOV version 2.0-1