简体   繁体   English

flutter - bloc - 如何在我的 Ui 中使用 FutureBuilder 来正确实现 Bloc 架构

[英]flutter - bloc - how can I use FutureBuilder in my Ui to properly implement Bloc Architecture

I am newbie to flutter and Bloc architecture and I am trying to use Bloc for my login functionality.我是 flutter 和 Bloc 架构的新手,我正在尝试将 Bloc 用于我的登录功能。 I am trying to make a call to a function in my Bloc file, but I do not know how to do that.我正在尝试调用我的 Bloc 文件中的函数,但我不知道该怎么做。 also I would be glad if you could help me to see if there is any other problems in my use of Bloc.如果您能帮我看看我在使用 Bloc 时是否还有其他问题,我也会很高兴。 here is the code of my Ui:这是我的 Ui 的代码:

MaterialButton(
                      color: Colors.deepPurple,
                      minWidth: screenAwareSize(500, context),
                      onPressed: () {
                        _submitForm(authBloc, user, pass);
                      },
 void _submitForm(AuthBloc authBloc, String user, String pass) async {
    formKey.currentState.save();
    if (formKey.currentState.validate()) {
      var response = await authBloc.login(user, pass);
//when I print(response) it shows null


    }
  }

here is my bloc class:这是我的集团班级:

class AuthBloc extends MainBloc {
  final Repo _repo = Repo();
  PublishSubject<Future<UserModel>> _authController = new PublishSubject<Future<UserModel>>();
  Observable<Future<UserModel>> get auth => _authController.stream;
  login(String user, String pass) async {

    Future<UserModel> item = await _repo.login(user, pass);
    _authController.sink.add(item);
  }

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

AuthBloc authBloc = new AuthBloc();

and here is my API class:这是我的 API 类:

class API{
 Future<UserModel> login(String user, String pass) async {
    var response =
        await client.get(base_url + "login.php?user=${user}&pass=${pass}");
    return UserModel.fromJSON(json.decode(response.body));
  }}

here is my repo class:这是我的回购类:

 class Repo {
    final API api = new API();
  login(String user, String pass) async => await api.login(user, pass);}

I will try to explain first what BLOC components should do as short as possible (and as trivial as possible).我将首先尝试解释 BLOC 组件应该做什么,尽可能短(并且尽可能琐碎)。

  • UI screen - obviously shows data to the user UI 屏幕 - 明显地向用户显示数据
  • BLOC (or the ViewModel) - decides HOW to display data to the user, do we make the text bold, do we show the error, do we go to next screen. BLOC(或 ViewModel) - 决定如何向用户显示数据,是否将文本加粗,是否显示错误,是否进入下一个屏幕。
  • Repo - decides WHAT data to display to the user (do we show the content from db, do we fetch it from API, do we show products that are red ?) Repo - 决定向用户显示什么数据(我们是否显示来自数据库的内容,我们是否从 API 获取它,我们是否显示红色的产品?)

You can have other components too, based on what your app does, like:根据您的应用程序的功能,您也可以拥有其他组件,例如:

  • Networking - performs API requests and transforms response to a model, this should be accessible only from repos and the only thing that this component should do is receive data from the repo (headers, body, url) and return data to repo in the form of a model (you can check the code below).网络 - 执行 API 请求并将响应转换为模型,这应该只能从 repos 访问,并且该组件唯一应该做的就是从 repo(headers、body、url)接收数据并将数据以以下形式返回到 repo一个模型(您可以查看下面的代码)。
  • Database - execute CRUD operation on database, same accessible only from repo.数据库 - 对数据库执行 CRUD 操作,同样只能从 repo 访问。
  • Sensors - read data from native sensors, same accessible only from repo.传感器 - 从本机传感器读取数据,同样只能从 repo 访问。

Now, I would suggest to use the BLOC pattern with Dependency Injection too, without it is kind of useless.现在,我建议也将 BLOC 模式与依赖注入一起使用,否则它是无用的。 With DI, you can mock all components till UI and it would be very easy to unit test all your code.使用 DI,您可以模拟所有组件直到 UI,并且可以很容易地对所有代码进行单元测试。

Also, I think there's no point to mix RxDart(library) with Streams/Future(dart equivalent of RxDart lib).另外,我认为将 RxDart(库)与 Streams/Future(dart 等效于 RxDart 库)混合使用是没有意义的。 For start, I would suggest to use only one of it and based on your code snippet, I would suggest to have better look on how to use Rx overall.首先,我建议只使用其中一个,并且根据您的代码片段,我建议更好地了解如何使用 Rx。

So, below you have a small code snippet on how I would use the block patter to do a login(it would be nice to check the code comments too :) ).所以,下面你有一个关于我将如何使用块模式进行登录的小代码片段(检查代码注释也很好:))。

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

class TheUIScreen extends StatefulWidget {
  @override
  _TheUIScreenState createState() => _TheUIScreenState();
}

class _TheUIScreenState extends State<TheUIScreen> {
  //TODO: for repo, block, networking, we used dependecy injection, here we have to create and init all the dependecies;

  TheAuthBlock _block;

  @override
  void initState() {
    super.initState();
    TheAuthAPI api = TheAuthAPI();
    TheAuthRepo repo =
        TheAuthRepo(theAuthAPI: api); // we could also do repo = TheAuthRepo();
    _block =
        TheAuthBlock(repo: repo); // we could also do _block = TheAuthBlock();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: RaisedButton(onPressed: () {
        _block.loginUser("test", "test").then((actualUser) {
          Navigator.of(context).push(MaterialPageRoute(builder: (context) {
            return TestRoute(); // or do whatever action you need, the user is logged in
          }));
        }).catchError((error) {
          //show error, something went wrong at login;
        });
      }),
    );
  }
}

class TheAuthBlock {
  final TheAuthRepo repo;

  TheAuthBlock({this.repo = const TheAuthRepo()});

  Future<UserModel> loginUser(String email, String password) {
    return repo.login(email, password).then((userModel) {
      //TODO: here we decide HOW to display the user, you might want to transfor the UserModel into a model that's used only for UI.
      //In any way, here you should do all the processing, the UI only should only display the data, not manipulate it.
    });
  }
}

class TheAuthRepo {
  final TheAuthAPI theAuthAPI;

  const TheAuthRepo(
      {this.theAuthAPI =
          const TheAuthAPI()}); // THIS would be the default constructor but it will alow us to test it using unit tests.

  Future<UserModel> login(String email, String password) {
    //TODO: here you could also check if the user is already logged in and send the current user as a response
    if (email.isNotEmpty && password.isNotEmpty) {
      return theAuthAPI.login(email, password).then((userModel) {
        //TODO: you can do extra processing here before returning the data to the block;
      });
    } else {
      return Future.error(
          "Well you can't login with empty ddata"); // TODO: you can return differetn errors for email or pwd;
    }
  }
}

class TheAuthAPI {
  final String url;

  const TheAuthAPI({this.url = "https://my.cool.api/login"});

  Future<UserModel> login(String email, String pwd) {
    // TODO: note you return a future from this method since the login will return only once (like almost all http calls)
    Map<String, String> headers = Map(); // TODO: set any headers you need here
    Map<String, String> body = {
      "email": email,
      "pwd": pwd
    }; // TODO: change the body acordingly
    return http.post("THE URL", headers: headers, body: body).then((response) {
      //TODO: parse response here and return it
      return UserModel("test",
          "test"); // this should be generated from the response not like this
    });
  }
}

class UserModel {
  final String email;

  UserModel(this.email, this.pass);

  final String pass;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM