首页 文章

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

提问于
浏览
1

所以我有一个登录页面,当用户按下登录时,中间件进行API调用,收到响应后,reducer会更改应用程序状态( isLogged in设置为 true ) . 如何使用Navigator转到下一页 . 当我尝试这样做时,抛出了错误,即在构建期间调用了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 回答

  • 2

    您可以将Completer添加到 LoginAction ,这样它将有3个字段: username ,_01248484_和 completer .

    当您将此完成符传递给 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() .

相关问题