flutterauthenticationjwtinherited-widget

Update value of variable of inherited widget


This is my login code for the app I am making. I want to update token in parent_inherit(Inherited Widget) which i am getting after posting the request.But I am not able to understand how to update value of the variable of Inherited widget explicity without that build method or parent_inherit widget.I want to change token value inside Inherited widget(parent_inherit).

Future<void> attemptLogIn(String username, String password,BuildContext context) async {
  String parentToken= parent_inherit.of(context);
  SharedPreferences prefs = await SharedPreferences.getInstance();
  print("$username $password");
  final http.Response res = await http.post(
      "..............the route.............",
      headers: <String, String>{
        'Content-Type': 'application/json; charset=UTF-8',
      },
      body: jsonEncode(<String, String>{
        "email": username,
        "password": password
      }),
  );
  if(res.statusCode == 200) {
    prefs.setString('jwt',res.body);
    var value=prefs.getString('jwt');
    Map<String,dynamic> valueMap=jsonDecode(value);
    TokenClass token=TokenClass.fromJson(valueMap);


    parentToken=token.token;// I was trying something like this,but it seems to not work


    Navigator.of(context).pushNamed('/home');
  }
  else{
    return _showMyDialoglogin(context,res.statusCode);
  }
}

Also,in above file when i declare parentToken above and use it down then it still shows that parentToken declared above is not being used.It also shows the warning when i hover over it.Why so?

parent_inherit.dart

class parent_inherit extends InheritedWidget {
  final String token;

  const parent_inherit({
    Key key,
    @required this.token,
    Widget child})
      :super(key:key,child: child);

  @override
  bool updateShouldNotify(parent_inherit oldWidget) =>true;

  static String of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<parent_inherit>().token;
}

TokenClass.dart

class TokenClass{
  final String token;

  TokenClass(this.token);

  TokenClass.fromJson(Map<String,dynamic> json)
  : token=json['token'];

}

My main function - In main function there is an issue with token value ,it is not able to properly update and get the value both inside and outside of inherited widget.Sometimes it uses previous token value too.

main.dart

bool checkingKey;
String tokenName;
TokenClass token;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  Paint.enableDithering = true;
  const defaultJson = '{}';
  var prefs = await SharedPreferences.getInstance();
  checkingKey = prefs.containsKey("jwt");
  

  tokenName=prefs.getString('jwt');
  
  Map<String, dynamic> valueMap = jsonDecode(tokenName??defaultJson);
  token=TokenClass.fromJson(valueMap);
 
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return parent_inherit(
      token: checkingKey==false?'':token.token,
      child: MaterialApp(
        home: AnnotatedRegion<SystemUiOverlayStyle>(
          value: SystemUiOverlayStyle(
            statusBarColor: Colors.transparent,
          ),
          child: Scaffold(
            resizeToAvoidBottomInset: false,
            body: Container(
              color: Color(0xffccffcc),
              child: !checkingKey ? LoginPage() : mainPage(),
            ),
          ),
        ),
        routes: <String,WidgetBuilder>{
          '/home':(BuildContext context) => mainPage(),
          '/login':(BuildContext context) => LoginPage(),
        },
      ),
    );
  }
}

Other suggestion are welcome too...


Solution

  • One way to update an InheritedWidget is by using a Notification.

    First, we have our Notification Class:

    class LoginNotification extends Notification {
      final TokenClass token;
    
      LoginNotification(this.token);
    }
    

    Then we wrap the parent_inherit with a notification listener:

    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      @override
      Widget build(BuildContext context) {
        return NotificationListener<LoginNotification>(
          onNotification: (loginNotification) {
            setState(() {
              token = loginNotification.token;
              checkingKey = true;
            });
            return true;
          },
          child: parent_inherit(
            token: checkingKey ? token.token : '',
            child: MaterialApp(
              home: AnnotatedRegion<SystemUiOverlayStyle>(
                value: SystemUiOverlayStyle(
                  statusBarColor: Colors.transparent,
                ),
                child: Scaffold(
                  resizeToAvoidBottomInset: false,
                  body: Container(
                    color: Color(0xffccffcc),
                    child: checkingKey ? MainPage() : LoginPage(),
                  ),
                ),
              ),
              routes: <String, WidgetBuilder>{
                '/home': (BuildContext context) => MainPage(),
                '/login': (BuildContext context) => LoginPage(),
              },
            ),
          ),
        );
      }
    }
    

    And in the attemptLogIn method, you just need to dispatch a notification:

    Future<void> attemptLogIn(
        String username, String password, BuildContext context) async {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      if (kDebugMode) print("$username $password");
      final http.Response res = await http.post(
        "..............the route.............",
        headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
        },
        body: jsonEncode(<String, String>{"email": username, "password": password}),
      );
      if (res.statusCode == 200) {
        // check if the login is valid first
        //...
    
        prefs.setString('jwt', res.body);
        var value = prefs.getString('jwt');
        Map<String, dynamic> valueMap = jsonDecode(value);
        TokenClass token = TokenClass.fromJson(valueMap);
    
        // send notification to a LoginNotification Listener
        LoginNotification(token).dispatch(context);
    
        Navigator.of(context).pushNamed('/home');
      } else {
        return _showMyDialoglogin(context, res.statusCode);
      }
    }
    

    As you see, using InheretedWidget is not that easy, and all of that was for a single value.

    Maybe consider other state management solutions, I don't have any particular recommendations but the flutter docs has a list of the most used ones here.