Home > Mobile >  Issue removing token while logging out in flutter
Issue removing token while logging out in flutter

Time:03-20

I have little experience in terms of keeping an app logged in using a token and then removing that token when the user hits Sign Out. Although I have managed to keep the app in a logged-in state, the trouble begins when I press the Sign Out button. Clicking on Sign out appears as if it is logged out. This is also the case when the app is only minimized, but as soon as the app is cleared from memory and started again, it takes me directly into the home screen. I believe the token is somehow not getting cleared off the local memory when I log out. Also, I keep getting the below error every time I start the app and logout. The code is below:

The error I get says:

Unhandled Exception: Null check operator used on a null value

The provider that facilitates Sign In, Sign Up and Sign Out.

class Network with ChangeNotifier {
  String url = 'https://achievexsolutions.in/current_work/eatiano/';
  var token;
  var _authToken = '';
  bool _isAuth = false;

  bool get auth {
    return _isAuth;
  }

  Future<String?> get authToken async {
    SharedPreferences localStorage = await SharedPreferences.getInstance();
    return _authToken = localStorage.getString('token')!;    //This is where the above error points at
  }

  getToken() async {
    SharedPreferences localStorage = await SharedPreferences.getInstance();
    token = localStorage.getString('token')!;
  }

  authData(data, apiUrl) async {
    var fullUrl = url   apiUrl;
    return await http.post(Uri.parse(fullUrl),
        body: jsonEncode(data), headers: setHeaders());
  }

  logOut(apiUrl) async {               //This is the Sign Out API
    var fullUrl = url   apiUrl;
    SharedPreferences localStorage = await SharedPreferences.getInstance();
    localStorage.remove('token');      //This is where I remove the token
    return await http.get(Uri.parse(fullUrl), headers: setHeaders());
  }

  getData(apiUrl) async {
    var fullUrl = url   apiUrl;
    await getToken();
    return await http.get(Uri.parse(fullUrl), headers: setHeaders());
  }

  setHeaders() => {
        'Content-type': 'application/json',
        'Accept': 'application/json',
        'Authorization': 'Bearer $token'
      };
}

The Sign In method in widget:

class SignInFormState extends State<SignInForm> {
  final _passwordFocus = FocusNode();
  final _key = GlobalKey<FormState>();
  late var inputEmail;
  late var inputPassword;

  @override
  void dispose() {
    _passwordFocus.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      ......
      ......
                  Container(
                    decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(30),
                        boxShadow: const [
                          BoxShadow(
                              color: Colors.black,
                              blurRadius: 20,
                              spreadRadius: 10,
                              offset: Offset(2, 1))
                        ]),
                    child: InkWell(
                        onTap: () {
                          if (_key.currentState!.validate()) {
                            _login();
                          }
                        },
                        child: Button('Login')),
                  ),
  }

  void _login() async {
    final data = {'email': inputEmail, 'password': inputPassword};
    var res = await Provider.of<Network>(context, listen: false)
        .authData(data, 'api/auth/login');
    final provider =
        Provider.of<LocationProvider>(context, listen: false).loading;
    print(json.decode(res.body));
    var body = json.decode(res.body);

    if (body['error'] == 'Unauthorized') {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: Text('Email or Password invalid',
            style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
        backgroundColor: Colors.white,
        action: SnackBarAction(
            label: 'Close',
            onPressed: () => Scaffold.of(context).hideCurrentSnackBar()),
      ));
    } else {
      SharedPreferences localStorage = await SharedPreferences.getInstance();
      localStorage.setString('token', body['access_token']);         //This is where I set the token
      Navigator.of(context).pushReplacementNamed('/bottom-bar');
    }
  }
}

The main.dart file where the token is checked:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => Network()),
      ],
      builder: (context, child) {
        final provider = Provider.of<LocationProvider>(context).loading;
        final authToken = Provider.of<Network>(context).authToken;
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
              scaffoldBackgroundColor: const Color.fromRGBO(25, 29, 33, 1),
              primaryColor: const Color.fromRGBO(252, 17, 17, 1)),
          home: authToken != null ? BottomNavigation() : SignIn(),   //This check doesn't seem to work
          routes: {
            '/sign-in': (context) => SignIn(),
            '/sign-up': (context) => SignUp(),
            '/otp-screen': (context) => OTP(),
            '/bottom-bar': (context) => BottomNavigation(),
          },
        );
      },
    );
  }
}

Logout Widget:

class Profile extends StatefulWidget {
  ProfileState createState() => ProfileState();
}

class ProfileState extends State<Profile> {

  @override
  Widget build(BuildContext context) {

    // TODO: implement build
    return Scaffold(
      body: Center(
        ......
        ......
        ......
            SizedBox(height: height * 0.003),
            Center(
              child: Padding(
                padding: EdgeInsets.only(left: width * 0.04),
                child: InkWell(
                  onTap: () async {
                    var res = await Provider.of<Network>(context, listen: false)
                        .logOut('api/auth/logout');    //This is where the LogOut method Is invoked
                    Navigator.of(context).pushReplacementNamed('/sign-in');
                  },
                  child: Text(
                    'Sign Out',
                    textScaleFactor: textScale,
                    style: const TextStyle(
                        color: Colors.white,
                        fontWeight: FontWeight.bold,
                        fontSize: 12),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Any suggestion as to how the logout functionality can be facilitated properly would be appreciated. Also, everyone is open to suggest ways to improve the code.

CodePudding user response:

Can you try adding "await" in front of localstorage in the signout function? Maybe not adding await can be the problem since its an asynchronous operation. I can be wrong too!

CodePudding user response:

I guess this is about auth_token on home parametres. auth_token cant be null. Can you use (auth_token.isNotEmpty) ?

  • Related