Edit: solved by adding a unique key
to the widget constructor
call:
Edit 2: as Randal Schwartz mentioned -> this is not a solution. Issue NOT solved and still open.
@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,
);
I have an abstract class
(ApiEndpoint) with two implementation
s (ApiEndpointName, ApiEndpointUniversal).
Both implementation
s have a StatefulWidget
(SearchTermInputOneString) inherit
ed from the same base class
(SearchTermInput).
For both ApiEndpoint-objects the Widget part of SearchTermInputOneStringis created separately – what is correct.
My problem is, that the State-part of SearchTermInputOneString is created only once – so ApiEndpointName and ApiEndpointUniversal own the same state.
I have to read ApiEndpoint-specific configuration during initState
– that does not work because initState
is called only for the first ApiEndpoint-instance π
Any ideas how to solve that?
abstract class
ApiEndpoint:
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 implementation:
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) {
...
}
ApiEndpointUniversal implementation:
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;
}
Implementation:
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]);
}
},
);
}
}
I create the List of ApiEndpoints this way:
List<ApiEndpoint> getApiEndPoints({required WidgetRef widgetRef}) {
final List<ApiEndpoint> apiEndpoints = [
ApiEndpointName(widgetRef: widgetRef),
ApiEndpointUniversal(widgetRef: widgetRef),
];
return apiEndpoints;
}
2
Answers
Okay, I changed my code but unfortunately I now got the same problem - the state is not updated for the second Widget-Instance (only if the first instance is disposed before!) :(
This is how the code now looks:
Widget that changes the widgets (for "Search via Name" and "Universal Search"):
BaseClass for the ApiEndpoint implementations:
ApiEndpoint-Class that owns the widget for the first instance of the widget (Search via name):
ApiEndpoint-Class that owns the widget for the second instance of the widget (Universal search):
BaseClass of the Widget:
Widget implementation for ApiEndpointName and ApiEndpointUniversal:
Logoutput after the first instance has been created:
Logoutput after the second instance:
That's it - no
create
, noinitState
(what seems to be correct after deleting the unique key), nodidChangeDependencies
:( - and the text of the texteditingcontroller is still the one of the first instance :(I am at the very beginning of my βflutter careerβ and don't want to learn anything wrong. I am therefore very grateful for your advice!
After playing around a lot of time without getting
didChangeDependencies
to work I found out that didUpdateWidget did the trick. Do you think that this okay or is there another stumbling block that I overlooked?