So im trying to implement graphql in flutter, using bloc too. I think i already did all the necessary part, but when i try to hit the graphql, it return response like this
HttpLinkParserException (ResponseFormatException(originalException: FormatException: Unexpected character (at character 1)
here is my graphql function –
void _onLoginButtonPressed(
LoginButtonPressed event, Emitter<LoginState> emit) async {
emit(LoginLoading());
try {
// Define your login mutation
final String loginMutation = '''
mutation Login($uname: String!, $pass: String!, $idNum: String!, $deviceId: String!) {
login(uname: $uname, pass: $pass, idNum: $idNum, deviceId: $deviceId) {
accessToken
refreshToken
}
}
''';
// Define variables for your mutation
final Map<String, dynamic> variables = {
'uname': event.username,
'pass': event.password,
'idNum': event.idNum,
'deviceId': event.deviceId,
};
// Execute the login mutation and get the response
final QueryResult result = await graphQLClient.mutate(
MutationOptions(
document: gql(loginMutation),
variables: variables,
),
);
if (result.hasException) {
print(result.exception!);
} else {
// Extract access token and refresh token from the response
final accessToken = result.data?['login']['accessToken'];
final refreshToken = result.data?['login']['refreshToken'];
emit(
LoginSuccess(accessToken: accessToken, refreshToken: refreshToken));
}
} catch (e) {
emit(LoginFailure(error: e.toString()));
}
}
this is the response when I try using postman
{
"data": {
"login": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVbmFtZSI6ImRlbyIsIlJlZ2lzdGVyZWRDbGFpbXMiOnsiaXNzIjoiYnJtb2JpbGUiLCJleHAiOjE2OTY5MDkwNTZ9fQ.XT57BIVfR6Ct8IIf5zHicqhwIqF_J9Ckj9FWflzQdwc",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVbmFtZSI6ImRlbyIsIlJlZ2lzdGVyZWRDbGFpbXMiOnsiaXNzIjoiYnJtb2JpbGUiLCJleHAiOjE2OTY5MjM0NTZ9fQ.MTBtgeKe9cgrPE4CnCPG7Z4vagi9_7qm4PDT4aJ6Nbo"
}
}
}
this is my graphql setup, on the main file:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initializes Hive with a valid directory in your app files
await Hive.initFlutter();
await Hive.openBox("userBox");
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GraphQLClient client = GraphQLClient(
link: HttpLink('https://127.0.0.1:7130/graphql/'),
cache: GraphQLCache(),
);
final AuthLink authLink = AuthLink(
getToken: () async {
final box = Hive.box('userBox');
final token = box.get('accessToken');
return 'Bearer $token';
},
);
late final ValueNotifier<GraphQLClient> clientNotifier =
ValueNotifier<GraphQLClient>(client);
@override
Widget build(BuildContext context) {
return GraphQLProvider(
client: clientNotifier,
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
routes: {
'/home': (context) => const Scaffold(
body: Center(
child: Text('Home'),
),
),
},
home: BlocProvider(
create: (context) => LoginBloc(graphQLClient: client),
child: LoginScreen(),
),
),
);
}
}
This is my widget where I use the bloc
class _LoginScreenState extends State<LoginScreen> {
TextEditingController usernameController = TextEditingController();
TextEditingController passwordController = TextEditingController();
TextEditingController idNumController = TextEditingController();
TextEditingController deviceIDController = TextEditingController();
@override
Widget build(BuildContext context) {
final loginBloc = BlocProvider.of<LoginBloc>(context);
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('Login'),
),
body: Center(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: BlocBuilder<LoginBloc, LoginState>(
builder: (context, state) {
if (state is LoginLoading) {
return const CircularProgressIndicator();
} else if (state is LoginSuccess) {
print('object');
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) =>
HomeScreen()), // Replace with your desired screen
);
} else if (state is LoginFailure) {
return Text('Login Failed: ${state.error}');
}
return Container(
child: Column(
children: [
TextField(
controller: usernameController,
decoration: const InputDecoration(
labelText: 'Username',
),
),
TextField(
controller: passwordController,
decoration: const InputDecoration(
labelText: 'Password',
),
),
TextField(
controller: idNumController,
decoration: const InputDecoration(
labelText: 'IdNum',
),
),
TextField(
controller: deviceIDController,
decoration: const InputDecoration(
labelText: 'DeviceId',
),
),
ElevatedButton(
onPressed: () {
final username = usernameController.text;
final password = passwordController.text;
final deviceId = deviceIDController.text;
final idNum = idNumController.text;
loginBloc.add(LoginButtonPressed(
username: username,
password: password,
idNum: idNum,
deviceId: deviceId,
));
Navigator.pop(context);
},
child: const Text('Login'),
),
],
),
);
},
),
)
],
),
),
);
}
}
9
1 Answer
Below is a solution (you might need to fix some imports) where the build is being forced to wait for the graphclient to be created.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initializes Hive with a valid directory in your app files
await Hive.initFlutter();
await Hive.openBox("userBox");
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<ValueNotifier<GraphQLClient>> buildGraphClient() async {
await initHiveForFlutter();
final HttpLink httpLink = HttpLink('https://127.0.0.1:7130/graphql/');
final AuthLink authLink = AuthLink(
getToken: () async {
final box = Hive.box('userBox');
final token = box.get('accessToken');
return 'Bearer $token';
},
);
final Link link = authLink.concat(httpLink);
return ValueNotifier(
GraphQLClient(
cache: GraphQLCache(store: HiveStore()),
link: link,
),
);
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: buildGraphClient(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return GraphQLProvider(
client: snapshot.data,
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
routes: {
'/home': (context) => const Scaffold(
body: Center(
child: Text('Home'),
),
),
},
home: BlocProvider(
create: (context) => LoginBloc(graphQLClient: client),
child: LoginScreen(),
),
),
);
}
}
return Center(
child: CircularProgressSpinner(),
);
}
)
}
5
-
on the blocProvider, what should i pass inside loginbloc parameter?
– foreverLearnerDevsyesterday
-
oh whoops forgot to update that, should be the same
snapshot.data
i think. But depending on what type the input is you could also doGraphQLProvider.of(context).value
if you get a type error.– CStarkyesterday
-
sir, now i got this error
Tried to listen to an InheritedWidget in a life-cycle that will never be called again.
– foreverLearnerDevsyesterday
-
from what i seeing looks it good be an issue somewhere in the blockProvider
– CStark13 hours ago
-
I think i put the blocProvider at the top widget (in main file) why its still error
– foreverLearnerDevs3 hours ago
what does it print to the console when you do
print(result.data)
Since i know graph likes to nest it results pretty weirdlyyesterday
it throws error after the result, the code stops there
yesterday
ah ok, so your graphqlclient setup could be the issue. Can you include how you setup the graphqlclient, you can remove any sensitive urls. Also are you using graphql or graphql-flutter package?
yesterday
Ok I will edit and Include it, I think so too. I tested it on the local server tho, so it's fine. Im using GraphQL-Flutter, is there any difference between the two of it?
yesterday
I edited the question @CStark
yesterday