How to use provider notification when my user logs in?

Issue

I’m new to flutter and provider. I only want to change from login to home widget after the user logs in. I have a wrapper widget that listen to the user model if it was changed and will automatically show authentication widget or the home widget. The code below does not notify the wrapper that the user is not null after i logged in. I don’t know it this is the correct way of using it but i hope you can help me.

//main.dart

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<User>.value(
      value: User(),
      child: MaterialApp(),
        home: Wrapper(),
      ),
    );
  }
}

//wrapper.dart

class Wrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);
    //return either home or authenticate widget
    print(user);
    if (user == null) {
      return Authenticate();
    } else {
      return Home();
    }
  }
}

//authenticate.dart

class Authenticate extends StatefulWidget {
  @override
  _AuthenticateState createState() => _AuthenticateState();
}

class _AuthenticateState extends State<Authenticate> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Signin(),
    );
  }
}

class Signin extends StatefulWidget {
  @override
  _SigninState createState() => _SigninState();
}

class _SigninState extends State<Signin> {
  final AuthService _auth = AuthService();
  @override
  Widget build(BuildContext context) {
    return Container(
      child: MaterialButton(
          onPressed: () async {
            if (_formKey.currentState.validate()) {
              dynamic result = await _auth.login(username, password);
            }
          },
      )
    )



//auth.dart

class AuthService {

  static const endpoint = 'http://192.168.254.100:8000';

  var client = new http.Client();

  Future login(String user, String password) async {
    try {
      var response = await client.get('$endpoint/rest_login/?username=test&password=test');

      return User.fromJson(json.decode(response.body));

    } catch (e) {
      return null;
    }
  }
}

//user.dart

class User with ChangeNotifier {
  int id;
  String name;
  String username;
  String email;
  String session;

  User({this.id, this.name, this.username, this.email, this.session});

  User.initial()
      : id = 0,
        name = '',
        username = '',
        email = '',
        session = '';

  User.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    name = json['name'];
    username = json['username'];
    email = json['email'];
    session = json['session'];
    notifyListeners();
  }
}

Solution

User.fromJson is an constructor. Don’t call notifyListeners() there.

You have to specify Consumer widgets for the model, So that it can get rebuild when the model calls notifyListeners().

This may help,

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<UserModel>.value(
      value: UserModel(),
      child: MaterialApp(
        home: Wrapper(),
      ),
    );
  }
}

//wrapper.dart

class Wrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //return either home or authenticate widget
    return Consumer<UserModel>(
      builder: (context, userModel, child) {
        if (userModel.user == null) {
          return Authenticate();
        } else {
          return Home();
        }
      },
    );
  }
}

//authenticate.dart

class Authenticate extends StatefulWidget {
  @override
  _AuthenticateState createState() => _AuthenticateState();
}

class _AuthenticateState extends State<Authenticate> {
  @override
  Widget build(BuildContext context) {
    return Material(
      child: SignIn(),
    );
  }
}

class SignIn extends StatefulWidget {
  @override
  _SignInState createState() => _SignInState();
}

class _SignInState extends State<SignIn> {
  final AuthService _auth = AuthService();

  @override
  Widget build(BuildContext context) {
    return Container(
      child: MaterialButton(
        child: Text("Login"),
        onPressed: () async {
          final user = await _auth.login("Name", "Password");
          Provider.of<UserModel>(context, listen: false).user = user;
        },
      ),
    );
  }
}

//auth.dart
class AuthService {
  static const endpoint = 'http://192.168.254.100:8000';

  var client = new http.Client();

  Future<User> login(String user, String password) async {
    // Use your logic and return User or null
    return User(name: "Name");
  }
}

//user.dart
class User {
  int id;
  String name;
  String username;
  String email;
  String session;

  User({this.id, this.name, this.username, this.email, this.session});

  User.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    name = json['name'];
    username = json['username'];
    email = json['email'];
    session = json['session'];
  }
}

//home.dart
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text("Home Page"),
      ),
    );
  }
}

//user_model.dart
class UserModel extends ChangeNotifier {
  User _user;

  User get user => _user;

  set user(User value) {
    _user = value;

    //here the model value changes. you can call 'notifyListeners' to notify all the 'Consumer<UserModel>'
    notifyListeners();
  }
}

Answered By – Crazy Lazy Cat

Answer Checked By – Katrina (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.