build method

  1. @override
Widget build(
  1. BuildContext context
)
override

Describes the part of the user interface represented by this widget.

The framework calls this method when this widget is inserted into the tree in a given BuildContext and when the dependencies of this widget change (e.g., an InheritedWidget referenced by this widget changes). This method can potentially be called in every frame and should not have any side effects beyond building a widget.

The framework replaces the subtree below this widget with the widget returned by this method, either by updating the existing subtree or by removing the subtree and inflating a new subtree, depending on whether the widget returned by this method can update the root of the existing subtree, as determined by calling Widget.canUpdate.

Typically implementations return a newly created constellation of widgets that are configured with information from this widget's constructor and from the given BuildContext.

The given BuildContext contains information about the location in the tree at which this widget is being built. For example, the context provides the set of inherited widgets for this location in the tree. A given widget might be built with multiple different BuildContext arguments over time if the widget is moved around the tree or if the widget is inserted into the tree in multiple places at once.

The implementation of this method must only depend on:

If a widget's build method is to depend on anything else, use a StatefulWidget instead.

See also:

  • StatelessWidget, which contains the discussion on performance considerations.

Implementation

@override
Widget build(BuildContext context) {
  if (getDropDownOptions == null) {
    return _buildTextField(context);
  }

  return LayoutBuilder(
    builder: (context, constraints) {
      return FutureBuilder<List<String>>(
        future: getDropDownOptions!(),
        builder: (context, snapshot) {
          // 在窗口尺寸变化或布局变化时,自动重算方向(通过 LayoutBuilder 触发 build)
          final openDirection = _computeOpenDirection(context);

          // 首帧后:在自动模式下,计算并保存方向,用于未弹出时也能正确显示箭头
          if (showListCandidateBelow == null) {
            WidgetsBinding.instance.addPostFrameCallback((_) {
              final dir = _computeOpenDirection(context);
              if (controller.optionsDirection != dir) {
                controller.setOptionsDirection(dir);
              }
            });
          }

          return RawAutocomplete<String>(
            textEditingController: textController,
            focusNode: controller.focusNode,
            optionsViewOpenDirection: openDirection,
            optionsBuilder: (TextEditingValue textEditingValue) {
              if (!enabled) return <String>[];

              // 如果刚刚选择了一个选项,不显示下拉列表
              if (controller.justSelected) {
                controller.updateCurrentOptions([]);
                return <String>[];
              }

              final allOptions = snapshot.data ?? <String>[];

              // 当由“获得焦点”或“点击箭头”触发,且要求始终展示全量候选时,忽略输入进行过滤
              if (controller._lastOpenTrigger != _OpenTrigger.typing &&
                  showAllOnPopWithNonTyping) {
                controller.updateCurrentOptions(allOptions);
                return allOptions;
              }

              if (textEditingValue.text.isNotEmpty) {
                final filteredOptions = allOptions
                    .where(
                      (option) =>
                          _filterOption(option, textEditingValue.text),
                    )
                    .toList();

                // 更新控制器中的选项列表,用于键盘导航
                controller.updateCurrentOptions(filteredOptions);
                return filteredOptions;
              }
              controller.updateCurrentOptions(allOptions);
              return allOptions;
            },
            onSelected: (String selected) {
              controller.markJustSelected();
              onOptionSelected?.call(selected);
              controller.setDropdownOpen(false);
              controller.clearHighlight();
            },
            optionsViewBuilder: (context, onSelected, options) {
              // 只有在用户没有手动关闭下拉列表时才显示
              final shouldShow =
                  options.isNotEmpty && !controller.manuallyClosedDropdown;
              // 避免在build期间触发响应式更新,延迟到frame结束
              WidgetsBinding.instance.addPostFrameCallback((_) {
                controller.setDropdownOpen(shouldShow);
              });
              final dropdownWidth = constraints.maxWidth;

              // 让 RawAutocomplete 控制展开方向(3.35+),我们仅提供内容和尺寸
              return _buildDropdownList(
                context,
                onSelected,
                options,
                dropdownWidth,
              );
            },
            fieldViewBuilder: (
              context,
              textEditingController,
              focusNode,
              onFieldSubmitted,
            ) {
              return _buildTextField(
                context,
                onSuffixIconTap: () {
                  if (enabled) {
                    // 记录触发来源为“箭头点击”并清除手动关闭标记
                    controller._lastOpenTrigger = _OpenTrigger.arrow;
                    controller.resetManuallyClosedFlag();

                    // 若当前未获得焦点,先请求焦点
                    if (!focusNode.hasFocus) {
                      focusNode.requestFocus();

                      // 当 showAllOnPopWithNonTyping 为 false 且文本非空时,焦点回调不会触发“轻微文本变更”,
                      // 因此在此处补一次以打开(保持按输入过滤的语义)。
                      if (!showAllOnPopWithNonTyping) {
                        final currentText = textEditingController.text;
                        textEditingController.text = ' ';
                        Future.microtask(() {
                          textEditingController.text = currentText;
                          textEditingController.selection =
                              TextSelection.collapsed(
                            offset: currentText.length,
                          );
                        });
                      }
                      // 不调用 onFieldSubmitted,避免与焦点回调产生重复触发
                      return;
                    }

                    // 已经获得焦点:直接触发一次轻微文本变更以打开下拉
                    final currentText = textEditingController.text;
                    textEditingController.text = ' ';
                    Future.microtask(() {
                      textEditingController.text = currentText;
                      textEditingController.selection =
                          TextSelection.collapsed(
                        offset: currentText.length,
                      );
                    });
                    onFieldSubmitted();
                  }
                },
              );
            },
          );
        },
      );
    },
  );
}