将Redux与API调用结合使用时如何在Flutter中打开下一页

时间:2018-07-23 07:55:29

标签: dart flutter

所以我有一个登录页面,当用户按下登录名时,中间件进行API调用,并且在收到响应后,reducer更改应用程序状态(将isLogged设置为true)。如何使用导航器转到下一页。当我尝试执行此操作时,将引发在构建过程中调用setResult()的错误。 当状态更改时,将重新构建小部件树,以便导航器无法获取适当的上下文。如何等待树重建,然后调用Navigator.of(context).push(Route)。

import 'package:flutter/material.dart';
import 'package:vattendance_flutter/common/loading_status.dart';
import 'package:vattendance_flutter/common/ui/buttons/primary_button.dart';
import 'package:vattendance_flutter/common/ui/loading/primary_circular_progress.dart';
import 'package:vattendance_flutter/common/ui/text_fields.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:vattendance_flutter/redux/app/app_state.dart';
import 'package:redux/redux.dart';
import 'package:vattendance_flutter/redux/login/login_actions.dart';
import 'package:vattendance_flutter/ui/home/home_page.dart';

class LoginPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  _ViewModel _viewModel;
  TextEditingController _usernameController;
  TextEditingController _passwordController;

  GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  void initState() {
    super.initState();
    _usernameController = TextEditingController();
    _passwordController = TextEditingController();
  }

  @override
  Widget build(BuildContext context) {
    return StoreConnector<AppState, _ViewModel>(
      converter: (store) => _ViewModel.fromStore(store),
      builder: (context, viewModel) {
        _viewModel = viewModel;
        _stateDependentSetUp();
        return StoreBuilder<AppState>(
          rebuildOnChange: true,
          onDidChange: (store) {},
          builder: (context, viewModel) {
            return Scaffold(
              key: scaffoldKey,
              resizeToAvoidBottomPadding: false,
              body: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'VAttendance',
                    style: TextStyle(
                      fontSize: 40.0,
                      fontWeight: FontWeight.w300,
                    ),
                  ),
                  Padding(
                    padding: EdgeInsets.only(
                      top: 40.0,
                      left: 16.0,
                      right: 16.0,
                    ),
                    child: PrimaryTextField(
                      controller: _usernameController,
                      hintText: "Username",
                    ),
                  ),
                  Padding(
                    padding: EdgeInsets.only(
                      top: 40.0,
                      left: 16.0,
                      right: 16.0,
                    ),
                    child: PrimaryTextField(
                      controller: _passwordController,
                      hintText: "Password",
                      obsecureText: true,
                    ),
                  ),
                  Container(
                    margin: EdgeInsets.only(top: 40.0),
                    child: _getLoginButtonOrProgress(),
                  ),
                ],
              ),
            );
          },
        );
      },
    );
  }

  _stateDependentSetUp() {
    var loadingData = _viewModel.loadingData;
    if (_viewModel.loadingData.status == LoadingStatus.ERROR) {
      scaffoldKey.currentState.showSnackBar(
          SnackBar(content: Text(loadingData.message ?? "Error")));
    } else if (_viewModel.loadingData.status == LoadingStatus.SUCCESS) {
      scaffoldKey.currentState
          .showSnackBar(SnackBar(content: Text("Welcome to VAttendance.")));
      _startHome();
    }
  }

  _getLoginButtonOrProgress() {
    return _viewModel.loadingData.status == LoadingStatus.LOADING
        ? PrimaryCircularProgress(
            progressColor: Colors.blue,
          )
        : PrimaryButton(
            text: "Login",
            onPressed: () {
              FocusScope.of(context).requestFocus(FocusNode());
              _viewModel.onLoginPressed(
                username: _usernameController.text,
                password: _passwordController.text,
              );
            });
  }

  _startHome() {
         Navigator.of(scaffoldKey.currentContext).pushReplacement(
            MaterialPageRoute(
              builder: (context) {
                return HomePage();
              },
            ),
          );
  }
}

class _ViewModel {
  final bool isLoggedIn;
  final LoadingData loadingData;
  final Function({String username, String password, Function onError})
      onLoginPressed;

  _ViewModel({
    this.loadingData,
    this.onLoginPressed,
    this.isLoggedIn,
  });

  factory _ViewModel.fromStore(Store<AppState> store) {
    return _ViewModel(
      isLoggedIn: store.state.loginState.isLoggedIn,
      loadingData: store.state.loginState.loadingData,
      onLoginPressed: ({String username, String password, Function onError}) {
        store.dispatch(LoginAction(username: username, password: password));
      },
    );
  }
}

1 个答案:

答案 0 :(得分:3)

您可以将Completer添加到LoginAction中,这样它将具有3个字段:usernamepasswordcompleter

当您将此完成程序传递给LoginAction时:

Completer _initCompleter() {
  return new Completer()
    ..future.then((_) => Navigator.of(context).pushNamed("\someRoute"));
}

完成者将等到Future到达那里。
在中间件中,您可以调用complete()方法来告诉小部件转到下一个屏幕。

这是中间件逻辑内部的内容:

authService.loginInWithEmail(action.email, action.password).then(
  (user) {
    store.dispatch(new UserProvidedAction(user));
    action.completer?.complete();
  },
);

您还可以使用它来处理错误,只需将其添加到完成程序的将来的onError参数中,然后在中间件中可以调用completeError()