Describe the bug
My init, graphqlprovider and query are all in TabOneWidget, for which you can assume, its a TAB1 from Parent widget Tabs.
I'm not sure if I implemented something wrong or not but... everytime I switch tabs and come back to Tab1 where I do my Init etc, I'm getting this error and the Query of course, stops working.
E/flutter ( 351): [ERROR:flutter/shell/common/shell.cc(181)] Dart Error: Unhandled exception:
E/flutter ( 351): 'package:graphql_flutter/src/widgets/query.dart': Failed assertion: line 77 pos 12: 'client != null': is not true.
E/flutter ( 351): #0 _AssertionError._doThrowNew (dart:core/runtime/liberrors_patch.dart:40:39)
E/flutter ( 351): #1 _AssertionError._throwNew (dart:core/runtime/liberrors_patch.dart:36:5)
E/flutter ( 351): #2 QueryState.getQueryResult (package:graphql_flutter/src/widgets/query.dart:77:12)
E/flutter ( 351): <asynchronous suspension>
E/flutter ( 351): #3 QueryState.initState (package:graphql_flutter/src/widgets/query.dart:51:5)
E/flutter ( 351): #4 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3763:58)
E/flutter ( 351): #5 ComponentElement.mount (package:flutter/src/widgets/framework.dart:3629:5)
E/flutter ( 351): #6 Element.inflateWidget (package:flutter/src/widgets/framework.dart:2919:14)
E/flutter ( 351): #7 Element.updateChild (package:flutter/src/widgets/framework.dart:2722:12)
E/flutter ( 351): #8 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3665:16)
E/flutter ( 351): #9 Element.rebuild (package:flutter/src/widgets/framework.dart:3507:5)
E/flutter ( 351): #10 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3634:5)
E/flutter ( 351): #11 ComponentElement.mount (package:flutter/src/widgets/framework.dart:3629:5)
E/flutter ( 351): #12 ParentDataElement.mount (package:flutter/src/widgets/framework.dart:3967:11)
E/flutter ( 351): #13 Element.inflateWidget (package:flutter/src/widgets/framework.dart:2919:14)
E/flutter ( 351): #14 MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4771:32)
E/flutter ( 351): #15 Element.inflateWidget (package:flutter/src/widgets/framework.dart:2919:14)
E/flutter ( 351): #16 Element.updateChild (package:flutter/src/widgets/framework.dart:2722:12)
E/flutter ( 351): #17 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3665:16)
E/flutter ( 351): #18 Element.rebuild (package:flutter/src/widgets/framework.dart:3507:5)
E/flutter ( 351): #19 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3634:5)
E/flutter ( 351): #20 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3781:11)
E/flutter ( 351): #21 ComponentElement.mount (package:flutter/src/widgets/framework.dart:3629:5)
E/flutter ( 351): #22 Element.inflateWidget (package:flutter/src/widgets/framework.dart:2919:14)
E/flutter ( 351): #23 Element.updateChild (package:flutter/src/widgets/framework.dart:2722:12)
E/flutter ( 351): #24 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3665:16)
E/flutter ( 351): #25 Element.rebuild (package:flutter/src/widgets/framework.dart:3507:5)
E/flutter ( 351): #26 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3634:5)
E/flutter ( 351): #27 ComponentElement.mount (package:flutter/src/widgets/framework.dart:3629:5)
E/flutter ( 351): #28 Element.inflateWidget (package:flutter/src/widgets/framework.dart:2919:14)
E/flutter ( 351): #29 Element.updateChild (package:flutter/src/widgets/framework.dart:2722:12)
E/flutter ( 351): #30 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3665:16)
E/flutter ( 351): #31 Element.rebuild (package:flutter/src/widgets/framework.dart:3507:5)
E/flutter ( 351): #32 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3634:5)
E/flutter ( 351): #33 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3781:11)
E/flutter ( 351): #34 ComponentElement.mount (package:flutter/src/widgets/framework.dart:3629:5)
E/flutter ( 351): #35 Element.inflateWidget (package:flutter/src/widgets/framework.dart:2919:14)
E/flutter ( 351): #36 Element.updateChild (package:flutter/src/widgets/framework.dart:2722:12)
E/flutter ( 351): #37 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3665:16)
E/flutter ( 351): #38 Element.rebuild (package:flutter/src/widgets/framework.dart:3507:5)
E/flutter ( 351): #39 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3634:5)
E/flutter ( 351): #40 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3781:11)
E/flutter ( 351): #41 ComponentElement.mount (package:flutter/src/widgets/framework.dart:3629:5)
E/flutter ( 351): #42 Element.inflateWidget (package:flutter/src/widgets/framework.dart:2919:14)
E/flutter ( 351): #43 Element.updateChild (package:flutter/src/widgets/framework.dart:2722:12)
E/flutter ( 351): #44 SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4666:14)
E/flutter ( 351): #45 Element
Expected behavior
To not throw an error?
Additional context
GraphqlProvider really have to be a parent of MaterialApp or can it be parent of one of my sub-widgets?Any help would be awesome.
@lordgreg Yeah you should wrap the MaterialApp with the GraphqlProvider, and not wrap individual pages.
Hi @HofmannZ and thank you for your response. I thought this would solve the issue I'm having above but it didn't.
MaterialApp with GraphqlProvider and CacheProvider. static init() {
client = ValueNotifier(
Client(
endPoint: Static.BASE_URL_GRAPHQL,
cache: InMemoryCache(),
apiToken: Auth.accessToken,
),
);
}
Immediately after it, I navigate to the Widget which includes the Query widget. The query widget is called from private function:
Widget _diagramData() {
print("do we have a client? ${GraphQl.client.value}"); // show proper instance of client with my data.
return new Query(GraphQl.getMyDayData(), builder: ({
bool loading,
Map data,
Exception error,
}) {
if (error != null) {
return Text(error.toString());
}
if (loading) {
return Center(child: CircularProgressIndicator());
}
Data.myDayData = MyDayData.fromJson(data["overviewChart"]);
return StackedAreaLineChart.withGraphQlData(Data.myDayData);
});
}
And this is ALWAYS throwing the exception above. Printing the client above, doesn't show any issues with client not being set-up.
Please keep in mind the query still get shown but that exception is just pain since it cannot be catched.
Is there some kind of possible catchError we can implement for that?
@lordgreg You should always provide a client to the GraphqlProvider. After the user logs in you can update the api token so that the user can access you api.
Right now the assertion in client != null fails inside the Query widget. See line 2 of your logs.
Hi @HofmannZ and thank you once again.
I've tried that too.
I've defined client as the docs specify, before using GraphqlProvider (without apiToken). After that, after logging in, I've updated it:
GraphQl.client.value.apiToken = Auth.accessToken;
Navigator.pushReplacementNamed(context, "/dashboard"); // query here still says Client is null (as you've saw above).
When do you call your static init function?
@HofmannZ
class App extends StatelessWidget {
final _appLocale = const Locale('de', 'DE');
@override
Widget build(BuildContext context) {
GraphQl.init(); // <------
lockOrientation();
return new GraphqlProvider(
client: GraphQl.client,
child: CacheProvider(
child: new MaterialApp(
GraphQl class:
class GraphQl {
static ValueNotifier<Client> client;
static init() {
client = ValueNotifier(
Client(
endPoint: Static.BASE_URL_GRAPHQL,
cache: InMemoryCache(),
apiToken: Auth.accessToken,
),
);
}
}
Login process:
Auth.doLogin(userField.text, passwordField.text).then((user) {
print("done $user");
GraphQl.client.value.apiToken = Auth.accessToken;
Navigator.pushReplacementNamed(context, "/dashboard");
}
Assuming you call Auth.doLogin from somewhere within the widget three. Could you try to move the value notifier out of the GraphQl class, directly into the build class. And then make the following change to the login process:
-GraphQl.client.value.apiToken = Auth.accessToken;
+GraphqlProvider.of(context).value.apiToken = Auth.accessToken;
Tried. Sadly, the same issue.
What version are you running?
graphql_flutter: ^0.9.1
Hi @HofmannZ,
I still did not solved the issue but I'm now using
Client client = GraphQl.client.value;
client
.query(query: GraphQl.getTariffInformation())
to fetch the data and upon it, show the data retrieved.
This is an work-around, suitable for me. I think we can close the issue?
@lordgreg Hmm, well it's not really solved yet.
I think the problem is the way you setup the client, where the Query widget is build before the GraphqlProvider has received the client.
To confirm this can you change the the Provider to this:
return GraphqlProvider(
client: ValueNotifier(
Client(
endPoint: Static.BASE_URL_GRAPHQL,
cache: InMemoryCache(),
apiToken: Auth.accessToken,
),
),
child: CacheProvider(
child: new MaterialApp(
...
If that's the case you might want to move GraphQl.init() into a initState() method.
@lordgreg did you have a change to check that out?
After migrating from 0.7.0 to 0.9.3 I've noticed this issue happening for me too, but in my case, the query runs successfully.
The error/stacktrace is printed at the console, but the query completes normally after the second execution of getQueryResult()
This is related to this commit:
https://github.com/zino-app/graphql-flutter/commit/c9a1bd96b6a08a5bf1ebd1926a8c251d0a8c0387
The flow that happens is:
QueryState.initState()executesQueryState.initState()callsgetQueryResult()getQueryResult()throws the assert error because client is nullQueryState.didChangeDependencies()is called due toStatefulElement._firstBuild()QueryState.clientgets updatedQueryState.getQueryResult()is called again due toQueryState.build(fromStatefulElement.build()
I think that maybe setting the value of the client in the initState method could resolve this. I'm a beginner at Flutter so I don't know if this would be a good practice. If this would produce another error due to context usage.
@lucastsantos I think we can just drop getQueryResult from the initState method. Its being called in the build method anyways.
Or just upgrade to 1.0.0 ✌🏻
I've upgraded to 1.0.0-alpha and the problem is gone 😄
I liked these new structures, it improves the support for lesser common use cases.
(e.g. before 1.0.0-alpha, I had to create a class that extends the client to override the default headers).
Great work! 🙇
Fix on it's way in #92.
:tada: This issue has been resolved in version 1.0.0-beta.1 :tada:
The release is available on GitHub release
Your semantic-release bot :package::rocket:
:tada: This issue has been resolved in version 1.0.0 :tada:
The release is available on GitHub release
Your semantic-release bot :package::rocket:
Most helpful comment
@lucastsantos I think we can just drop
getQueryResultfrom theinitStatemethod. Its being called in the build method anyways.Or just upgrade to 1.0.0 ✌🏻