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 : }
|