0

I'm new to flutter and graphql I'm trying to develop an app to consume backend api hosted on heroku during development and while testing the app on my Samsung Note 5, the app connects successfully to the backend and I can fetch data from query and mutation but after building the app-release.apk and installing it on the same mobile, the response is always null in both the query and mutation. Also, I have REST api within the backend and I use Dio lib to send requests but I don't get any response from these requests. I tried "flutter clean" but in vain flutter doctor command shows no error:

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.0.3, on Microsoft Windows [Version 10.0.18363.1646], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[√] Chrome - develop for the web
[√] Android Studio
[√] VS Code (version 1.54.3)
[√] Connected device (2 available)

• No issues found!

I'm using android studio 4.2.2

main.dart

bool isAuth;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initHiveForFlutter();
  isAuth = await isAuthenticated();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GraphQLProvider(
      client: client,
      child: MaterialApp(
          debugShowCheckedModeBanner: false,
          title: 'Flutter App',
          theme: ThemeData(
            primarySwatch: Colors.red,
          ),
          initialRoute: isAuth ? 'HomeScreen' : 'LoginScreen',
          routes: {
            'HomeScreen': (context) => HomeScreen(),
            'LoginScreen': (context) => LoginScreen()
          }),
    );
  }
}

authentication.dart

// check if token is set using sharedPreferences lib
Future<bool> isAuthenticated() async {
  final accessToken = await getAccessToken(); 
  if (accessToken != null) {
    return true;
  }
  return false;
}

GraphQLConfiguration.dart:

class GraphQLConfiguration {

  HttpLink _httpLink;
  AuthLink _authLink;
  Link _link;

  GraphQLConfiguration () {
    _httpLink = new HttpLink('https://backendapi.herokuapp.com/graphql');
    _authLink = new AuthLink(getToken: () async => 'Bearer ${await getAccessToken()}');
    _link = _authLink.concat(_httpLink);
  }

  GraphQLClient myGQLClient() {
    return GraphQLClient(
      link: _link,
      cache: GraphQLCache(store: HiveStore()),
    );
  }
}
ValueNotifier<GraphQLClient> client = ValueNotifier(GraphQLConfiguration().myGQLClient());

mutations.dart

String login() {
    return '''
      mutation login(\$data: LoginInput!){
        login(data: \$data){
          status
          message
          accessToken
        } 
      }
    ''';
  }

LoginScreen.dart


class LoginScreen extends StatefulWidget {
  @override
  _LoginScreenState createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  bool _hidePassword = true;

  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      body: Stack(
        children: [
          Mutation(
            options: MutationOptions(
                document: gql(mutations.login()),
                update: (GraphQLDataProxy cache, QueryResult result) {
                  String cookie;
                  final String cookieHeader = result.context
                      .entry<HttpLinkResponseContext>()
                      .headers['set-cookie'];
                  if (cookieHeader != null) {
                    cookie = cookieHeader.split(';')[0];
                  }

                  final LoginResponse loginResponse =
                      LoginResponse.from(result.data['login']);

                  if (loginResponse.status == true && cookie != null) {
                    setRefreshToken(cookie);
                  }
                  return cache;
                },
                onCompleted: (resultData) async {
                  final LoginResponse loginResponse =
                      LoginResponse.from(resultData['login']);
                  if (loginResponse.status == true) {
                    await setAccessToken(loginResponse.accessToken);
                    Navigator.pushReplacement(context,
                        MaterialPageRoute(builder: (context) => HomeScreen()));
                  }
                  Fluttertoast.showToast(
                    msg: loginResponse.message,
                    backgroundColor: Colors.grey,
                    gravity: ToastGravity.BOTTOM,
                    fontSize: 16.0,
                    timeInSecForIosWeb: 1,
                    textColor: Colors.white,
                    toastLength: Toast.LENGTH_LONG,
                  );
                }),
            builder: (RunMutation runMutation, QueryResult result) {
              return Column(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  Container(
                    margin: EdgeInsets.only(top: 100),
                    child: Column(
                      children: [
                        Text(
                          'Welcome...',
                          style: TextStyle(
                            shadows: [
                              Shadow(
                                offset: Offset(0.5, 0.5),
                                blurRadius: 10.0,
                                color: Colors.grey,
                              ),
                            ],
                            fontSize: 45,
                            letterSpacing: 2,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                        Text(
                          'welcome,,,',
                          style: TextStyle(
                            fontSize: 20,
                            letterSpacing: 7,
                          ),
                        )
                      ],
                    ),
                  ),
                  Container(
                    child: Column(
                      children: [
                        Padding(
                          padding:
                              EdgeInsets.symmetric(vertical: 0, horizontal: 20),
                          child: TextField(
                            controller: _usernameController,
                            style: TextStyle(
                              fontSize: 22.0,
                            ),
                            decoration: InputDecoration(
                              labelText: 'Username',
                              border: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(50.5),
                              ),
                              prefixIcon:
                                  Icon(Icons.supervised_user_circle_outlined),
                            ),
                          ),
                        ),
                        Padding(
                          padding: EdgeInsets.symmetric(
                              vertical: 10, horizontal: 20),
                          child: TextField(
                            controller: _passwordController,
                            obscureText: _hidePassword,
                            style: TextStyle(
                              fontSize: 22.0,
                            ),
                            decoration: InputDecoration(
                              labelText: 'Password',
                              border: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(50.5),
                              ),
                              prefixIcon: Icon(Icons.lock),
                              suffixIcon: IconButton(
                                icon: Icon(_hidePassword
                                    ? Icons.visibility
                                    : Icons.visibility_off),
                                onPressed: () => _togglePasswordVisibility(),
                              ),
                            ),
                          ),
                        ),
                        Padding(
                          padding: EdgeInsets.only(top: 30),
                          child: ElevatedButton(
                            style: ButtonStyle(
                              padding: MaterialStateProperty.all<EdgeInsets>(
                                  EdgeInsets.all(15)),
                              minimumSize:
                                  MaterialStateProperty.all<Size>(Size(370, 0)),
                              shape: MaterialStateProperty.all<
                                  RoundedRectangleBorder>(
                                RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(50.0),
                                  side: BorderSide(color: Colors.red),
                                ),
                              ),
                              backgroundColor:
                                  MaterialStateProperty.all<Color>(Colors.red),
                            ),
                            onPressed: () => runMutation(
                              {
                                'data': {
                                  'username': _usernameController.text,
                                  'password': _passwordController.text
                                }
                              },
                            ),
                            child: Text(
                              'Login',
                              style: TextStyle(
                                fontSize: 30.0,
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                  Container(
                    margin: EdgeInsets.only(top: 100),
                    padding: EdgeInsets.only(bottom: 20),
                    child: Center(
                      child: Text(
                        'BY XXX',
                        style: TextStyle(
                          color: Colors.grey,
                          fontWeight: FontWeight.bold,
                          fontSize: 10,
                        ),
                      ),
                    ),
                  ),
                ],
              );
            },
          ),
        ],
      ),
    );
  }

  void _togglePasswordVisibility() {
    setState(() {
      _hidePassword = !_hidePassword;
    });
  }

pubspec.yaml


environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  graphql_flutter: ^5.0.0
  shared_preferences: ^2.0.6
  http: ^0.13.3
  fluttertoast: ^8.0.7
  font_awesome_flutter: ^9.1.0
  dio: ^4.0.0
  flutter_datetime_picker: ^1.5.1
  loading_overlay: ^0.3.0

  cupertino_icons: ^1.0.2


dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:

  uses-material-design: true


  assets:
     - assets/images/male.png
     - assets/images/female.png

  fonts:
    - family: Changa
      fonts:
        - asset: fonts/Changa-Regular.ttf
        - asset: fonts/Changa-Bold.ttf
          weight: 700

m.a
  • 61
  • 1
  • 5

1 Answers1

0

As I said, I'm new to flutter and mobile app development and the solution was obtained from this thread

The problem wasn't related to flutter_graphql lib but to the manifest file of the android project. internet permission should be declared. I supposed flutter will auto-detect and configure the app permissions based on the used libraries, but I was wrong.

m.a
  • 61
  • 1
  • 5