Rx.combineLatest2 не работает должным образом - PullRequest
1 голос
/ 07 марта 2020

Я новичок ie в использовании флаттера. Я пытаюсь создать образец приложения во флаттере для создания собственного приложения. Я нашел хорошее приложение во флаттере с шаблоном blo c. пожалуйста, смотрите следующую ссылку .

это просто страница входа. в форме есть адрес электронной почты и пароль с подтверждением. Один валидатор для электронной почты и другой валидатор для длины пароля. В форме есть кнопка отправки, изначально она отключена, если адрес электронной почты и пароль успешно подтверждены, тогда кнопка отправки включена. он использует шаблонную архитектуру blo c с пакетом rxdart .

У меня проблема с проверкой кнопки отправки, после ввода поля электронной почты и пароля она не активируется поле кнопки.

код подтверждения кнопки

Stream<bool> get submitCheck => 
  Rx.combineLatest2(email, password, (e, p) => true);

См. Нижеприведенный код.

main.dart

import 'package:bloc_login/pagetwo.dart';
import 'package:flutter/material.dart';
import 'package:bloc_login/bloc.dart';
import 'package:flutter/rendering.dart';

 void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
 @override
  Widget build(BuildContext context) {
return MaterialApp(
  home: MyHomePage(title: 'Flutter Demo Home Page'),
  title: 'Flutter Demo',
  theme: ThemeData(
    primarySwatch: Colors.teal,
  ),
  debugShowCheckedModeBanner: false,
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
 @override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
 changethePage(BuildContext context) {
Navigator.of(context)
    .push(MaterialPageRoute(builder: (context) => PageTwo()));
  }

 @override
   Widget build(BuildContext context) {
final bloc = Bloc();

return Scaffold(
  appBar: AppBar(
    title: Text("Bloc pattern"),
  ),
  body: SingleChildScrollView(
    child: Container(
      height: MediaQuery.of(context).size.height,
      padding: EdgeInsets.all(16),
      child: Column(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          StreamBuilder<String>(
            stream: bloc.email,
            builder: (context, snapshot) => TextField(
              onChanged: bloc.emailChanged,
              keyboardType: TextInputType.emailAddress,
              decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: "Enter Email",
                  labelText: "Email",
                  errorText: snapshot.error),
            ),
          ),
          SizedBox(
            height: 20,
          ),
          StreamBuilder<String>(
            stream: bloc.password,
            builder: (context, snapshot) => TextField(
              onChanged: bloc.passwordChanged,
              keyboardType: TextInputType.text,
              obscureText: true,
              decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: "Enter password",
                  labelText: "Password",
                  errorText: snapshot.error),
            ),
          ),
          SizedBox(
            height: 20,
          ),
          StreamBuilder<bool>(
              stream: bloc.submitCheck,
              builder: (context, snapshot) => RaisedButton(
                    color: Colors.tealAccent,
                    onPressed: (snapshot.data != null)
                        ? () => changethePage(context)
                        : null,
                    child: Text("Submit"),
                  ))
        ],
      ),
    ),
  ),
);
}
}

blo c .dart

import 'dart:async';
import 'package:bloc_login/validator.dart';
import 'package:rxdart/rxdart.dart';

class Bloc extends Object with Validators implements BaseBloc {
final _emailController = StreamController<String>();
final _passwordController = StreamController<String>();

Function(String) get emailChanged => _emailController.sink.add;
Function(String) get passwordChanged => _passwordController.sink.add;

Stream<String> get email => _emailController.stream.transform(emailValidator);
Stream<String> get password =>
  _passwordController.stream.transform(passwordValidator);

Stream<bool> get submitCheck =>
  Rx.combineLatest2(email, password, (e, p) => true);

@override
void dispose() {
_emailController.close();
_passwordController.close();
}
}

 abstract class BaseBloc {
   void dispose();
}

validator.dart

import 'dart:async';

mixin Validators {
 var emailValidator =
  StreamTransformer<String, String>.fromHandlers(handleData: (email, sink) {
if (email.contains("@")) {
  sink.add(email);
} else {
  sink.addError("Email is not valid.");
}
});

var passwordValidator = StreamTransformer<String, String>.fromHandlers(
  handleData: (password, sink) {
if (password.length > 4) {
  sink.add(password);
} else {
  sink.addError("Password length should be greater than 4.");
}
});
}

Пожалуйста, совет.

1 Ответ

3 голосов
/ 07 марта 2020

Combinelatest2 в порядке, проблема в том, что вы создаете новый blo c в каждой перестройке.

Поэтому создайте blo c в методе initState и утилизируйте блок blo c в методе dispose StatefulWidget.

Но у ваших контроллеров StreamController есть Stream, который поддерживает только одного подписчика, так что если вы хотите, чтобы Stream из StreamController можно прослушивать более одного раза, это должен быть широковещательный поток, один из способов сделать это - использовать конструктор StreamController .broadcast () .

PD Если вы создаете формы с шаблоном blo c, вы можете проверить flutter_form_blo c, это сэкономит вам много кода.


С версии 2.1 вам не нужно расширять объект, чтобы использовать миксин

class Bloc with Validators implements BaseBloc {
  final _emailController = StreamController<String>.broadcast();
  final _passwordController = StreamController<String>.broadcast();
  //...
}
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Bloc _bloc;

  @override
  void initState() {
    super.initState();
    _bloc = Bloc();
  }

  @override
  void dispose() {
    _bloc.dispose();
    super.dispose();
  }

  changethePage(BuildContext context) {
    Navigator.of(context)
        .push(MaterialPageRoute(builder: (context) => PageTwo()));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Bloc pattern"),
      ),
      body: SingleChildScrollView(
        child: Container(
          height: MediaQuery.of(context).size.height,
          padding: EdgeInsets.all(16),
          child: Column(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              StreamBuilder<String>(
                stream: _bloc.email,
                builder: (context, snapshot) => TextField(
                  onChanged: _bloc.emailChanged,
                  keyboardType: TextInputType.emailAddress,
                  decoration: InputDecoration(
                      border: OutlineInputBorder(),
                      hintText: "Enter Email",
                      labelText: "Email",
                      errorText: snapshot.error),
                ),
              ),
              SizedBox(
                height: 20,
              ),
              StreamBuilder<String>(
                stream: _bloc.password,
                builder: (context, snapshot) => TextField(
                  onChanged: _bloc.passwordChanged,
                  keyboardType: TextInputType.text,
                  obscureText: true,
                  decoration: InputDecoration(
                      border: OutlineInputBorder(),
                      hintText: "Enter password",
                      labelText: "Password",
                      errorText: snapshot.error),
                ),
              ),
              SizedBox(
                height: 20,
              ),
              StreamBuilder<bool>(
                  stream: _bloc.submitCheck,
                  builder: (context, snapshot) => RaisedButton(
                        color: Colors.tealAccent,
                        onPressed: (snapshot.data != null)
                            ? () => changethePage(context)
                            : null,
                        child: Text("Submit"),
                      ))
            ],
          ),
        ),
      ),
    );
  }
}

...