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

Posted

技术标签:

【中文标题】flutter - bloc - 我如何在我的 Ui 中使用 FutureBuilder 来正确实现 Bloc 架构【英文标题】:flutter - bloc - how can I use FutureBuilder in my Ui to properly implement Bloc Architecture 【发布时间】:2019-08-22 06:16:03 【问题描述】:

我是 Flutter 和 Bloc 架构的新手,我正在尝试将 Bloc 用于我的登录功能。我正在尝试调用 Bloc 文件中的函数,但我不知道该怎么做。如果您能帮我看看我在使用 Bloc 时是否还有其他问题,我也会很高兴。 这是我的 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


    
  

这是我的集团课程:

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();

这是我的 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));
  

这是我的回购类:

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

【问题讨论】:

var response = await authBloc.login(user, pass); 将为 null,因为 bloc.login 方法是动态类型,并且您不会返回任何内容 【参考方案1】:

我将首先尝试解释哪些 BLOC 组件应该尽可能简短(并且尽可能简单)。

UI 屏幕 - 显然向用户显示数据 BLOC(或 ViewModel)- 决定如何向用户显示数据,是否将文本加粗,是否显示错误,是否转到下一个屏幕。 Repo - 决定向用户显示哪些数据(我们是否显示来自 db 的内容,是否从 API 获取内容,是否显示红色产品?)

根据您的应用程序的功能,您也可以拥有其他组件,例如:

网络 - 执行 API 请求并将响应转换为模型,这应该只能从 repos 访问,并且该组件应该做的唯一事情是从 repo 接收数据(标题、正文、url)并将数据返回到 repo模型的形式(可以查看下面的代码)。 数据库 - 对数据库执行 CRUD 操作,同样只能从 repo 访问。 传感器 - 从本地传感器读取数据,同样只能从 repo 访问。

现在,我建议也将 BLOC 模式与依赖注入一起使用,没有它是没有用的。使用 DI,您可以模拟所有组件直到 UI,并且很容易对所有代码进行单元测试。

另外,我认为将 RxDart(库)与 Streams/Future(RxDart 库的 dart 等效项)混合是没有意义的。首先,我建议只使用其中一个,根据您的代码 sn-p,我建议更好地了解如何整体使用 Rx。

所以,下面有一个小代码 sn-p,说明我将如何使用块模式进行登录(最好也检查代码 cmets :))。

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;

【讨论】:

Answer 是对 bloc 模式的一个很好的概述。但问题是关于如何使用futurebuilder,与bloc模式相关

以上是关于flutter - bloc - 我如何在我的 Ui 中使用 FutureBuilder 来正确实现 Bloc 架构的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Flutter 中连续执行 bloc 事件?

如何使用flutter bloc模式通过分页加载数据列表?

flutter_bloc 4.0.0如何获取事件属性参数。

Bloc如何收听流并发出状态

Flutter BLoC 模式 - 如何在流事件后导航到另一个屏幕?

Flutter 使用流“RxDart”提交登录 Bloc