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 1 : 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 1 : @override
45 1 : 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 1 : FntSelectorCallback? getItemBuilder() => null;
52 :
53 1 : void onTap(BuildContext context) async {
54 2 : focusController.blur();
55 : FocusController.force = true;
56 1 : final result = await Navigator.push(
57 : context,
58 1 : MaterialPageRoute(
59 2 : builder: (context) => ListSelectorPage(
60 2 : options: widget.options,
61 2 : result: widget.value,
62 2 : tooltip: widget.tooltip ?? widget.hintText ?? '...',
63 1 : itemBuilder: getItemBuilder(),
64 : ),
65 : ),
66 : );
67 : FocusController.force = false;
68 3 : widget.setState(result);
69 3 : WidgetsBinding.instance.addPostFrameCallback((_) {
70 2 : focusController.onEditingComplete(this);
71 3 : FocusManager.instance.primaryFocus?.unfocus();
72 : });
73 : }
74 :
75 1 : @override
76 : Widget build(BuildContext context) {
77 3 : focusController = FocusWrapper.of(context) ?? FocusController();
78 1 : final indent = ThemeHelper.getIndent(1.5);
79 3 : final hintStyle = context.textTheme.tooltipMedium.copyWith(overflow: TextOverflow.ellipsis);
80 2 : final labelStyle = context.textTheme.tooltipSmall;
81 12 : K? item = widget.value != null ? widget.options.cast().where((e) => e.equal(widget.value?.id)).firstOrNull : null;
82 :
83 1 : return InkWell(
84 2 : autofocus: focusController.isFocused(this),
85 4 : focusNode: focusController.bind(this, context: context, value: widget.value),
86 1 : child: TapWidget(
87 2 : tooltip: widget.tooltip ?? '',
88 4 : onTap: () => widget.isDisabled ? null : onTap(context),
89 0 : onFocusChange: (v) => v ? focusController.scrollToFocusedElement(this) : null,
90 1 : child: Container(
91 : width: double.infinity,
92 2 : color: widget.isDisabled
93 0 : ? context.colorScheme.onSurface.withValues(alpha: 0.1)
94 2 : : context.colorScheme.fieldBackground,
95 2 : child: LayoutBuilder(builder: (_, constraints) {
96 1 : return RowWidget(
97 3 : chunk: [null, ThemeHelper.barHeight - ThemeHelper.getIndent()],
98 1 : maxWidth: constraints.maxWidth,
99 : indent: 0.0,
100 1 : children: [
101 1 : [
102 1 : Container(
103 1 : padding: EdgeInsets.fromLTRB(
104 1 : indent / 1.5,
105 2 : widget.withLabel && item != null ? 1 : indent,
106 1 : AppDesign.isRightToLeft() ? ThemeHelper.getIndent() : 0,
107 2 : widget.withLabel && item != null ? indent / 2 : indent,
108 : ),
109 3 : constraints: BoxConstraints(maxWidth: constraints.maxWidth - ThemeHelper.barHeight),
110 : child: item != null
111 2 : ? 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 1 : : item.build(context)
121 1 : : TextWrapper(
122 2 : widget.hintText ?? '...',
123 2 : style: widget.hintStyle ?? hintStyle,
124 : ),
125 : ),
126 : ],
127 1 : [
128 1 : Padding(
129 2 : padding: EdgeInsets.only(top: indent / 3),
130 1 : child: ExcludeFocus(
131 2 : child: widget.isDisabled
132 : ? ThemeHelper.emptyBox
133 1 : : IconButton(
134 2 : tooltip: widget.hintText ?? '...',
135 3 : 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 : }
|