当我在 Flutter 中提交表单时,GlobalKey<FormState>().currentState.save() 正在下降

GlobalKey<FormState>().currentState.save() is falling when I submit a form in Flutter

使用来自 rxdart: ^0.24.1

的 bloc

我正在尝试在 mysql 上保存对象。第一次尝试成功保存对象,第二次尝试使用新对象,它落在 formKey.currentState.save()。我正在使用 GlobalKey<FormState>() 以使用 Stream

验证表单

我的密码是

class DetailGamePage extends StatefulWidget {
  @override
  _DetailGameState createState() => _DetailGameState();
}

class _DetailGameState extends State<DetailGamePage> {
  final formKey = GlobalKey<FormState>();
  GameBloc gameBloc;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    if (gameBloc == null) {
      gameBloc = Provider.gameBloc(context);
    }
  }

  @override
  Widget build(BuildContext context) {
    Game _game = ModalRoute.of(context).settings.arguments;

    if (_game == null) {
      _game = Game(
          color: "#000000",
          description: "",
          env: "",
          isBuyIt: false,
          isOnBacklog: false);
    }

    return Scaffold(
        appBar: AppBar(
          iconTheme: IconThemeData(color: Colors.black),
          backgroundColor: Colors.white,
          title: Text(
            "Add Game",
            style: TextStyle(color: Colors.black),
          ),
          actions: [
            FlatButton(
                onPressed: () {
                  if (formKey.currentState.validate()) {
                    formKey.currentState.save();

                    Fluttertoast.showToast(msg: "Game saved");
                    setState(() {
                      gameBloc.saveOrUpdate(_game, gameBloc.name,
                          gameBloc.description, "listGame");
                    });
                    Navigator.pushReplacementNamed(context, "home");
                  }
                },
                child: Text(
                  (StringUtils.isNullOrEmpty(_game.id)) ? "Add" : "Update",
                  style: TextStyle(color: HexColor(_game.color), fontSize: 20),
                ))
          ],
        ),
        body: Form(
          key: formKey,
          child: Stack(children: <Widget>[
            _createBackground(context, _game),
            _createFormGame(context, _game, gameBloc)
          ]),
        ));
  }

  Widget _createBackground(BuildContext context, Game game) {
    final size = MediaQuery.of(context).size;

    final gradientTop = Container(
      height: size.height, //* 0.4,
      width: double.infinity,
      decoration: BoxDecoration(
          gradient: LinearGradient(
              colors: <Color>[HexColor(game.color), Colors.white])),
    );

    final circule = Container(
      width: 100.0,
      height: 100.0,
      decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(100.0),
          color: Color.fromRGBO(255, 255, 255, 0.1)),
    );

    return Stack(
      children: <Widget>[
        gradientTop,
        Positioned(
          child: circule,
          top: 90,
          left: 50,
        ),
        Positioned(
          child: circule,
          top: -40,
          right: -30,
        ),
        Container(
          padding: EdgeInsets.only(top: 80),
          child: Column(
            children: <Widget>[
              SizedBox(
                height: 10.0,
                width: double.infinity,
              ),
            ],
          ),
        )
      ],
    );
  }

  Widget _createFormGame(BuildContext context, Game game, GameBloc gameBloc) {
    final size = MediaQuery.of(context).size;
    return SingleChildScrollView(
      child: Column(
        children: <Widget>[
          SafeArea(
              child: Container(
            height: 80.0,
          )),
          Container(
              width: size.width * 0.85,
              padding: EdgeInsets.symmetric(vertical: 50.0),
              margin: EdgeInsets.symmetric(vertical: 30.0),
              decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(5.0),
                  boxShadow: <BoxShadow>[
                    BoxShadow(
                        color: Colors.black26,
                        blurRadius: 3.0,
                        offset: Offset(0.0, 5.0),
                        spreadRadius: 3.0)
                  ]),
              child: Column(
                children: <Widget>[
                  Text("Foto", style: TextStyle(fontSize: 20.0)),
                  SizedBox(
                    height: 50.0,
                  ),
                  _createNameImput(gameBloc, game),
                  _createDescriptionImput(gameBloc, game),
                  Divider(
                    height: 30,
                    color: HexColor(game.color),
                    indent: 30,
                    endIndent: 20,
                  ),
                  _createWasGameImput(gameBloc, game),
                  Divider(
                    height: 30,
                    color: HexColor(game.color),
                    indent: 30,
                    endIndent: 20,
                  ),
                  _createToTheBacklogImput(gameBloc, game),
                  SizedBox(height: 60),
                  _createDeleteButton(gameBloc, game),
                  SizedBox(height: 60),
                ],
              ))
        ],
      ),
    );
  }

  @override
  void dispose() {
    gameBloc?.dispose();
    super.dispose();
  }

  Widget _createWasGameImput(GameBloc gameBloc, Game game) {
    return StreamBuilder(
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        return Container(
            padding: EdgeInsets.symmetric(horizontal: 20.0),
            child: SwitchListTile(
              activeColor: HexColor(game.color),
              title: Text("Do you have it?"),
              value: game.isBuyIt,
              onChanged: (bool value) {
                setState(() {
                  game.isBuyIt = value;
                });
              },
              secondary: IconButton(
                icon: Icon(Icons.shopping_cart),
                onPressed: null,
                color: HexColor(game.color),
              ),
            ));
      },
    );
  }

  Widget _createToTheBacklogImput(GameBloc gameBloc, Game game) {
    return StreamBuilder(
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        return Container(
            padding: EdgeInsets.symmetric(horizontal: 20.0),
            child: SwitchListTile(
              activeColor: HexColor(game.color),
              title: Text("To the backlog?"),
              value: game.isOnBacklog,
              onChanged: (bool value) {
                setState(() {
                  game.isOnBacklog = true;
                });
              },
              secondary: IconButton(
                icon: Icon(Icons.list),
                onPressed: null,
                color: HexColor(game.color),
              ),
            ));
      },
    );
  }

  Widget _createNameImput(GameBloc gamebloc, Game game) {
    return Column(children: [
      Container(
        padding: EdgeInsets.symmetric(horizontal: 20.0),
        child: TextFormField(
          textCapitalization: TextCapitalization.sentences,
          initialValue: game.name,
          onSaved: (value) {
            gameBloc.setName(value);
          },
          keyboardType: TextInputType.text,
          decoration: InputDecoration(
              labelText: "Name",
              icon: Icon(
                Icons.games,
                color: HexColor(game.color),
              )),
        ),
      ),
      Divider(
        height: 30,
        color: HexColor(game.color),
        indent: 30,
        endIndent: 20,
      ),
    ]);
  }

  Widget _createDescriptionImput(GameBloc gameBloc, Game game) {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 20.0),
      child: TextFormField(
        textCapitalization: TextCapitalization.sentences,
        initialValue: game.description,
        onSaved: (value) {
          gameBloc.setDescription(value);
        },
        keyboardType: TextInputType.text,
        decoration: InputDecoration(
            labelText: "Description",
            icon: Icon(
              Icons.description,
              color: HexColor(game.color),
            )),
      ),
    );
  }

  Widget _createDeleteButton(GameBloc gameBloc, Game game) {
    if (StringUtils.isNotNullOrEmpty(game.id)) {
      return FlatButton(
          onPressed: () {
            showDialog(
                context: context,
                builder: (context) {
                  return AlertDialog(
                    content: Text("Do you wan to remove the game"),
                    actions: <Widget>[
                      FlatButton(
                          onPressed: () {
                            setState(() {
                              gameBloc.remove(game, "listGame");
                            });
                            Navigator.pop(context);
                            Navigator.pop(context);
                          },
                          child: Text("Yes")),
                      FlatButton(
                          onPressed: () => Navigator.of(context).pop(),
                          child: Text("No"))
                    ],
                  );
                });
          },
          child: Text("Remove Game"));
    } else {
      return Container();
    }
  }
}

这就是集团

class GameBloc extends Validators {
  //Controller
  final _allDataGames = BehaviorSubject<List<Game>>();
  final _descriptionController = BehaviorSubject<String>();
  final _nameController = BehaviorSubject<String>();
  final _allMyListGamesByNameController = BehaviorSubject<List<Game>>();

  //Services
  GameService gameService = GameService();

  //get Data from streams
  Stream<List<Game>> get allGameData => _allDataGames.stream;
  Stream<List<Game>> get allGameByNameList =>
      _allMyListGamesByNameController.stream;
  Stream<String> get getDescriptionStream =>
      _descriptionController.stream.transform(validateDescription);

  Stream<String> get getNameStream =>
      _nameController.stream.transform(validName);

  //Observable
  Stream<bool> get validateDescriptionStream =>
      Rx.combineLatest([getDescriptionStream], (description) => true);

  Stream<bool> get validateNameStream =>
      Rx.combineLatest([getNameStream], (name) => true);

  //Set Stream
  Function(String) get setDescription => _descriptionController.sink.add;

  Function(String) get setName => _nameController.sink.add;

  //Get Stream

  //From repo
  void allGames() async {
    List<Game> games = await gameService.getAllDataGames();
    _allDataGames.sink.add(games);
  }

  //From my setting
  void allMyListGamesByName(String listName) async {
    List<Game> games = await gameService.allMyListGamesByName(listName);
    _allMyListGamesByNameController.sink.add(games);
  }

  void saveOrUpdate(
      Game game, String name, String description, String listGame) {
    game.name = name;
    game.description = description;
    if (StringUtils.isNullOrEmpty(game.id)) {
      game.id = Uuid().v1();
      gameService.add(game, listGame);
    } else {
      gameService.update(game);
    }
  }

  void remove(Game game, String listGame) {
    gameService.remove(game, listGame);
  }

  //Get Lastest stream value
  String get name => _nameController.value;
  String get description => _descriptionController.value;

  dispose() {
    _descriptionController?.close();
    _allMyListGamesByNameController?.close();
    _allDataGames?.close();
    _nameController?.close();
  }
}

提供商:

class Provider extends InheritedWidget {
  static Provider _imstance;
  final _gameBloc = GameBloc();

  factory Provider({Key key, Widget child}) {
    if (_imstance == null) {
      _imstance = new Provider._internal(key: key, child: child);
    }
    return _imstance;
  }

  Provider._internal({Key key, Widget child}) : super(key: key, child: child);

  static GameBloc gameBloc(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(Provider) as Provider)
        ._gameBloc;
  }

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return true;
  }
}

错误是:

════════ Exception caught by gesture ═══════════════════════════════════════════
Bad state: Cannot add new events after calling close

当我计算 formKey.currentState.save(); 时,我得到:

formKey.currentState.save()
Unhandled exception:
Bad state: Cannot add new events after calling close
#0      _BroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:249:24)
#1      Subject._add (package:rxdart/src/subjects/subject.dart:141:17)
#2      Subject.add (package:rxdart/src/subjects/subject.dart:135:5)
#3      _StreamSinkWrapper.add (package:rxdart/src/subjects/subject.dart:167:13)

我读到这个错误,它提到错误是在 Bloc singleston scopedispose 方法上。

发生了什么事?

当您使用 Navigator.pushReplacementNamed(context, "home") 导航到 home 时,正在处理 _DetailGamePage<State>,调用 gameBloc?.dispose。这使得 _gameBloc 实例化,所有 关闭。

由于您正在使用 Singleton Provider,当您导航回 DetailGamePage 时,您的 save 正在尝试写入到关闭的 streams.

您需要做的是将 streams 的关闭移动到小部件树的更上方,以免在您完成它们之前关闭它们,也许是在应用程序级别如果流关闭,则重新实例化_gameBloc,再次从存储库加载数据。