Search code examples
flutterblocrxdart

implementing simple rxDart with Bloc don't get result


from this link on my web server as

http://instamaker.ir/api/v1/getPersons

i'm trying to get result and printing avatar from that result, unfortunately my implementation with rxDart and Bloc don't get result from this response and i don't get any error

server response this simplified result:

{
  "active": 1,
  "name": "my name",
  "email": " 3 ",
  "loginType": " 3 ",
  "mobile_number": " 3 ",

  ...

  "api_token": "1yK3PvAsBA6r",
  "created_at": "2019-02-12 19:06:34",
  "updated_at": "2019-02-12 19:06:34"
}

main.dart file: (click on button to get result from server)

StreamBuilder(
  stream: bloc.login,
  builder: (context,
      AsyncSnapshot<UserInfo>
      snapshot) {
    if (snapshot.hasData) {
      parseResponse(snapshot);
    }
  },
);
void parseResponse(AsyncSnapshot<UserInfo> snapshot) {
  debugPrint(snapshot.data.avatar);
}

LoginBlock class:

class LoginBlock{
  final _repository = Repository();
  final _login_fetcher = PublishSubject<UserInfo>();

  Observable<UserInfo> get login=>_login_fetcher.stream;

  fetchLogin() async{
    UserInfo userInfo = await _repository.userInfo();
    _login_fetcher.sink.add(userInfo);
  }

  dispose(){
    _login_fetcher.close();
  }
}

final bloc = LoginBlock();

Repository class:

class Repository {
  final userInformation = InstagramApiProviders();

  Future<UserInfo> userInfo() => userInformation.checkUserLogin();
}

my model:

class UserInfo {
  int _active;
  String _name;
  ...

  UserInfo.fromJsonMap(Map<String, dynamic> map)
      : _active = map["active"],
        _name = map["name"],
        ...

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['active'] = _active;
    data['name'] = _name;
    ...

    return data;
  }

  //GETTERS
}

BaseUrl class:

class BaseUrl {
  static const url = 'http://instamaker.ir';
}

and then InstagramApiProviders class:

class InstagramApiProviders {
  Client client = Client();

  Future<UserInfo> checkUserLogin() async {
    final response = await client.get(BaseUrl.url+'/api/v1/getPersons');
    print("entered  "+BaseUrl.url+'/api/v1/getPersons');
    if (response.statusCode == 200) {
      return UserInfo.fromJsonMap(json.decode(response.body));
    } else
      throw Exception('Failed to load');
  }
}

Solution

  • Well the answer here is part of the test that I make to get this done. I can put my all test here but I think that the problem cause was because as StreamBuilder is a widget his builder method callback is only called when the widget is in flutter widget tree. As in your sample you're just creating a StreamBuilder the builder method will never be called bacause this widget isn't in widget tree.

    As advice first test your code changing only UI layer... do somenthing like:

    @override
      Widget build(BuildContext context) {
    
        return Scaffold(
          appBar: AppBar(
            actions: <Widget>[
              IconButton(icon: Icon(Icons.assessment), onPressed: () => loginBlock.fetchLogin()),
            ],
          ),
          body: StreamBuilder<UserInfo>(
            stream: loginBlock.login,
            builder: (context, snapshot){
              if (snapshot.hasData){
                parseResponse(snapshot);
                return Text('user: ${snapshot.data.name} ');
              }
              if (snapshot.hasError)
                return Text('${snapshot.error}');
              else return Text('There is no data');
            },
          ),
        );
    

    Here we're putting the StreamBuilder in widget tree so the builder callback is called and maybe you will see the results. If it fails, please comment that I update my answer with my full test code with this working.

    Updating the answer with sources that I made tests.

    Basic model

    class UserInfo {
      int _active;
      String name;
    
      UserInfo.fromJsonMap(Map<String, dynamic> map) {
        _active = map["active"];
        name = map["name"];
      }
    
      Map<String, dynamic> toJson() => {
        'active' : _active,
        'name' : name,
      };
    } 
    

    The provider class

    class InstagramApiProviders {
      Future<UserInfo> checkUserLogin() async {
    
        UserInfo info;
    
        try {
          http.Response resp = await http.get("http://instamaker.ir/api/v1/getPersons");
          if (resp.statusCode == 200){
            print('get response');
            print( resp.body );
            info = UserInfo.fromJsonMap(  Map.from(  json.decode(resp.body ) ));
          }
        }
        catch (ex) {
          throw ex;
        }
        print('returning $info');
        return info;
      }
    }
    

    Repository

    class Repository {
      final userInformation = InstagramApiProviders();
      Future<UserInfo> userInfo() => userInformation.checkUserLogin().then((user) => user);
    }
    

    BLoC class

    class LoginBlock{
      final _repository = Repository();
      final _login_fetcher = PublishSubject<UserInfo>();
    
      Observable<UserInfo> get login=>_login_fetcher.stream;
    
      fetchLogin() async {
        UserInfo info = await _repository.userInfo();
          _login_fetcher.sink.add(info);
      }
    
      dispose(){
        _login_fetcher.close();
      }
    }
    

    Widget UI

    This starts showing There is no data message but when you hit appBar button wait a little and then the data is fetched and updates the UI.

    class WidgetToShowData extends StatelessWidget {
    
      final LoginBlock bloc = LoginBlock();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            actions: <Widget>[
              IconButton(icon: Icon(Icons.assessment), onPressed: () => loginBlock.fetchLogin()),
            ],
          ),
          body: StreamBuilder<UserInfo>(
            stream: loginBlock.login,
            builder: (context, snapshot){
              if (snapshot.hasData){
                parseResponse(snapshot);
                return Text('user: ${snapshot.data.name} ');
              }
              if (snapshot.hasError)
                return Text('${snapshot.error}');
              else return Text('There is no data');
            },
          ),
        );
      }
    
      void parseResponse(AsyncSnapshot<UserInfo> snapshot) {
        debugPrint(snapshot.data.name);
      }
    }