编辑:通过向小部件
unique key
调用添加 constructor
来解决:
编辑 2:正如 Randal Schwartz 提到的 -> 这不是一个解决方案。问题仍未解决。
@override
SearchTermInput get searchTermInput => SearchTermInputOneString(
key: UniqueKey(),
currentSearchTermInputContent:
widgetRef.read(apiEndpointUniversalSearchTermInputContentProvider),
captionList: ["Statement"],
// todo : allow umlauts! or better deny numbers?
inputFormatterAllow: RegExp(".*"),
onSubmit: onSubmit,
onLostFocus: onLostFocus,
);
我有一个
abstract class
(ApiEndpoint) 和两个 implementation
(ApiEndpointName、ApiEndpointUniversal)。
两个
implementation
都有一个 StatefulWidget
(SearchTermInputOneString) inherit
来自同一个 base class
(SearchTermInput)。
对于两个 ApiEndpoint 对象,SearchTermInputOneString 的 Widget 部分是单独创建的 - 这是正确的。 我的问题是,SearchTermInputOneString 的状态部分仅创建一次 - 因此 ApiEndpointName 和 ApiEndpointUniversal 拥有相同的状态。
我必须在
initState
期间读取特定于 ApiEndpoint 的配置 - 这不起作用,因为仅针对第一个 ApiEndpoint 实例调用 initState
:(
有什么想法可以解决这个问题吗?
abstract class
API端点:
enum ApiEndpointType {
today,
unseen,
universal,
name,
exactAge,
ageBetween,
ageGreaterThanOrEqual,
ageLessThanOrEqual
}
abstract class ApiEndpoint {
ApiEndpointType get type;
String get title;
SearchTermInput get searchTermInput;
String get searchFunctionName;
bool validateData(BuildContext context, WidgetRef ref);
void onSubmit(
BuildContext context, WidgetRef ref, List<String> searchTermInputContent);
void onLostFocus(
BuildContext context, WidgetRef ref, List<String> searchTermInputContent);
}
ApiEndpointName 实现:
class ApiEndpointName implements ApiEndpoint {
final WidgetRef widgetRef;
ApiEndpointName({required this.widgetRef});
@override
String get searchFunctionName => "getObituariesByName";
@override
SearchTermInput get searchTermInput => SearchTermInputOneString(
currentSearchTermInputContent:
widgetRef.read(apiEndpointNameSearchTermInputContentProvider),
captionList: ["Name"],
inputFormatterAllow: RegExp("[a-zA-Z% ]"),
onSubmit: onSubmit,
onLostFocus: onLostFocus,
);
@override
String get title => "... by name";
@override
ApiEndpointType get type => ApiEndpointType.name;
@override
bool validateData(BuildContext context, WidgetRef ref) {
...
}
@override
void onSubmit(BuildContext context, WidgetRef ref,
List<String> searchTermInputContent) {
...
}
@override
void onLostFocus(BuildContext context, WidgetRef ref,
List<String> searchTermInputContent) {
...
}
ApiEndpoint通用实现:
class ApiEndpointUniversal implements ApiEndpoint {
final WidgetRef widgetRef;
ApiEndpointUniversal({required this.widgetRef});
@override
String get searchFunctionName => "getObituariesUniversal";
@override
SearchTermInput get searchTermInput => SearchTermInputOneString(
currentSearchTermInputContent:
widgetRef.read(apiEndpointUniversalSearchTermInputContentProvider),
captionList: ["Statement"],
inputFormatterAllow: RegExp(".*"),
onSubmit: onSubmit,
onLostFocus: onLostFocus,
);
@override
String get title => "... universal";
@override
ApiEndpointType get type => ApiEndpointType.universal;
@override
bool validateData(BuildContext context, WidgetRef ref) {
...
}
@override
void onSubmit(BuildContext context, WidgetRef ref,
List<String> searchTermInputContent) {
...
}
@override
void onLostFocus(BuildContext context, WidgetRef ref,
List<String> searchTermInputContent) {
...
}
}
abstract
StatefulWidget:
abstract class SearchTermInput extends ConsumerStatefulWidget {
const SearchTermInput({super.key});
List<String> get currentSearchTermInputContent;
List<String>? get captionList;
RegExp get inputFormatterAllow;
void Function(BuildContext, WidgetRef, List<String>) get onSubmit;
void Function(BuildContext, WidgetRef, List<String>) get onLostFocus;
}
实施:
class SearchTermInputOneString extends SearchTermInput {
@override
final List<String> currentSearchTermInputContent;
@override
final List<String> captionList;
@override
final RegExp inputFormatterAllow;
@override
final void Function(BuildContext, WidgetRef, List<String>) onSubmit;
@override
final void Function(BuildContext, WidgetRef, List<String>) onLostFocus;
const SearchTermInputOneString({
super.key,
required this.currentSearchTermInputContent,
required this.captionList,
required this.inputFormatterAllow,
required this.onSubmit,
required this.onLostFocus,
});
@override
ConsumerState<ConsumerStatefulWidget> createState() =>
_SearchTermInputOneStringState();
}
class _SearchTermInputOneStringState
extends ConsumerState<SearchTermInputOneString> {
late TextEditingController textEditingController;
@override
void initState() {
super.initState();
textEditingController = TextEditingController();
textEditingController.text = widget.currentSearchTermInputContent.isNotEmpty
? widget.currentSearchTermInputContent[0]
: "";
}
@override
void dispose() {
textEditingController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Focus(
child: TextFormField(
controller: textEditingController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: widget.captionList.isNotEmpty ? widget.captionList[0] : "",
suffixIcon: IconButton(
onPressed: () {
textEditingController.text = "";
},
icon: Icon(Icons.clear),
),
),
inputFormatters: [
FilteringTextInputFormatter.allow(widget.inputFormatterAllow)
],
onFieldSubmitted: (_) {
widget.onSubmit(context, ref, [textEditingController.text]);
},
),
onFocusChange: (hasFocus) {
if (!hasFocus) {
widget.onLostFocus(context, ref, [textEditingController.text]);
}
},
);
}
}
我用这种方式创建 ApiEndpoints 列表:
List<ApiEndpoint> getApiEndPoints({required WidgetRef widgetRef}) {
final List<ApiEndpoint> apiEndpoints = [
ApiEndpointName(widgetRef: widgetRef),
ApiEndpointUniversal(widgetRef: widgetRef),
];
return apiEndpoints;
}
好吧,我更改了代码,但不幸的是我现在遇到了同样的问题 - 没有为第二个小部件实例创建状态:(
这就是代码现在的样子:
第一个实例的调用:
@override
SearchTermInput get searchTermInput => SearchTermInputOneString(
// key: Key("ApiEndpointName"), // UniqueKey(),
apiEndpointSearchTermInputContentProvider:
apiEndpointSearchTermInputContentProvider,
captionList: ["Name"],
textInputFormatter: [
FilteringTextInputFormatter.deny(RegExp(r"[0-9]"))
],
onSubmit: onSubmit,
onLostFocus: onLostFocus,
);
第二个实例的调用:
@override
SearchTermInput get searchTermInput => SearchTermInputOneString(
// key: Key("ApiEndpointUniversal"), // UniqueKey(),
apiEndpointSearchTermInputContentProvider:
apiEndpointSearchTermInputContentProvider,
captionList: ["Statement"],
textInputFormatter: [FilteringTextInputFormatter.allow(RegExp(r".*"))],
onSubmit: onSubmit,
onLostFocus: onLostFocus,
);
StatefulWidget:
class SearchTermInputOneString extends SearchTermInput {
@override
final NotifierProvider apiEndpointSearchTermInputContentProvider;
@override
final List<String> captionList;
@override
final List<TextInputFormatter>? textInputFormatter;
@override
final void Function(BuildContext, WidgetRef, List<String>) onSubmit;
@override
final void Function(BuildContext, WidgetRef, List<String>) onLostFocus;
const SearchTermInputOneString({
super.key,
required this.apiEndpointSearchTermInputContentProvider,
required this.captionList,
required this.textInputFormatter,
required this.onSubmit,
required this.onLostFocus,
});
@override
// ignore: no_logic_in_create_state
ConsumerState<ConsumerStatefulWidget> createState() {
final log = getLogger();
log.t("createState");
return _SearchTermInputOneStringState();
}
}
class _SearchTermInputOneStringState
extends ConsumerState<SearchTermInputOneString> {
late TextEditingController textEditingController;
late List<String> currentContent;
@override
void initState() {
super.initState();
final log = getLogger();
log.t("initstate");
textEditingController = TextEditingController();
}
@override
void didChangeDependencies() {
_runsAfterInit(context, ref);
final log = getLogger();
log.t("didChangeDependencies");
// read apiEndpoint content
currentContent = ref
.read(widget.apiEndpointSearchTermInputContentProvider)
.cast<String>();
textEditingController.text =
currentContent.isNotEmpty ? currentContent[0] : "";
super.didChangeDependencies();
}
@override
void dispose() {
textEditingController.dispose();
final log = getLogger();
log.t("dispose");
super.dispose();
}
Future<void> _runsAfterInit(BuildContext context, WidgetRef ref) async {
await Future.delayed(Duration.zero); // <-- Add a 0 dummy waiting time
final log = getLogger();
log.t("_runsAfterInit");
// set current content
ref
.read(currentSearchTermInputContentProvider.notifier)
.setValue(currentContent);
}
@override
Widget build(BuildContext context) {
return Focus(
child: TextFormField(
controller: textEditingController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: widget.captionList.isNotEmpty ? widget.captionList[0] : "",
suffixIcon: IconButton(
onPressed: () {
textEditingController.text = "";
},
icon: Icon(Icons.clear),
),
),
inputFormatters: widget.textInputFormatter,
onFieldSubmitted: (_) {
widget.onSubmit(context, ref, [textEditingController.text]);
},
),
onFocusChange: (hasFocus) {
if (!hasFocus) {
widget.onLostFocus(context, ref, [textEditingController.text]);
}
},
);
}
}
创建第一个实例后的日志输出:
────────────────────────────────────────────────────────────────────────────────────────
#0 SearchTermInputOneString.createState (package:obituary_viewer/features/obituaries/presentation/widgets/search_term_inputs/search_term_input_one_string.dart:39:9)
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
2024-11-18 08:26:03.262
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
createState
────────────────────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────────────────
#0 _SearchTermInputOneStringState.initState (package:obituary_viewer/features/obituaries/presentation/widgets/search_term_inputs/search_term_input_one_string.dart:53:9)
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
2024-11-18 08:26:03.269
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
initstate
────────────────────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────────────────
#0 _SearchTermInputOneStringState.didChangeDependencies (package:obituary_viewer/features/obituaries/presentation/widgets/search_term_inputs/search_term_input_one_string.dart:61:9)
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
2024-11-18 08:26:03.272
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
didChangeDependencies
────────────────────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────────────────
#0 ApiEndpointNameSearchTermInputContent.build (package:obituary_viewer/features/obituaries/presentation/provider/search_term_input_content_provider.dart:26:9)
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
2024-11-18 08:26:03.275
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
ApiEndpointNameSearchTermInputContent provider build
────────────────────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────────────────
#0 DoSearchFunction.build (package:obituary_viewer/features/obituaries/presentation/provider/search_screen_provider.dart:13:9)
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
2024-11-18 08:26:03.402
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
DoSearchFunction provider build
────────────────────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────────────────
#0 DoSearchFunction.setValue (package:obituary_viewer/features/obituaries/presentation/provider/search_screen_provider.dart:19:9)
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
2024-11-18 08:26:03.404
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
DoSearchFunction set to Closure: (BuildContext, WidgetRef) => void from Function 'doSearch':.
────────────────────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────────────────
#0 _SearchTermInputOneStringState._runsAfterInit (package:obituary_viewer/features/obituaries/presentation/widgets/search_term_inputs/search_term_input_one_string.dart:85:9)
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
2024-11-18 08:26:03.405
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
_runsAfterInit
────────────────────────────────────────────────────────────────────────────────────────
第二个实例后的日志输出:
> flutter:
> ┌─────────────────────────────────────────────────────────────────────────────────────────
> flutter: │ #0 CurrentApiEndpoint.setValue
> (package:obituary_viewer/features/obituaries/presentation/provider/api_endpoint_provider.dart:30:9)
> flutter:
> ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
> flutter: │ 2024-11-18 08:27:41.773 flutter:
> ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
> flutter: │ CurrentApiEndpoints set to Instance of
> 'ApiEndpointUniversal' flutter:
> └─────────────────────────────────────────────────────────────────────────────────────────
就是这样 - 文本编辑控制器的文本仍然是第一个实例的文本:(