Я просто пробую флаттер, и я не могу заставить компоненты отображаться условно на основе BehaviourStream в моем блоке.
Я хочу сначала показать виджет "_buildPage ()" (который является формой авторизации), затем, когда _isLoading имеет значение true, но (_loginSucceded имеет значение false), я хочу показать счетчик. И наконец, когда _loginSucceded имеет значение true, а _isLoading имеет значение false, я хочу перенаправить пользователя.
Фактическое поведение - после отправки формы загрузчик показывает, как ожидалось. После того, как ответ успешно получен с сервера, аутентификация для снова отображается.
Я думаю, что моя логика в порядке, но кажется, что когда я устанавливаю значения потока в конструкторе Bloc, что-то еще вызывает перерисовку приложения, что приводит к нулевым значениям в потоке.
Есть ли способ убедиться, что поток всегда имеет базовые значения после того, как конструктор запустился после инициализации?
Или есть лучший способ управлять этим сценарием? Я только начал смотреть на Флаттера с JS-фона, поэтому я вполне могу что-то упустить.
Код блока:
import 'dart:async';
import 'dart:convert';
import 'package:rxdart/rxdart.dart';
import 'package:http/http.dart' as http;
import './auth_validator.dart';
class AuthBloc with AuthValidator {
final _email = BehaviorSubject<String>();
final _password = BehaviorSubject<String>();
final _isLoading = BehaviorSubject<bool>();
final _loginSucceded = BehaviorSubject<bool>();
AuthBloc() {
_isLoading.sink.add(false);
_loginSucceded.sink.add(false);
}
// stream getters
Stream<String> get email => _email.stream.transform(validateEmail);
Stream<String> get password => _password.stream.transform(validatePassword);
Stream<bool> get isLoading => _isLoading.stream;
Stream<bool> get loginSuccess => _loginSucceded.stream;
Stream<bool> get submitValid =>
Observable.combineLatest2(email, password, (e, p) => true);
// add data to sink onChange
Function(String) get emailChanged => _email.sink.add;
Function(String) get passwordChanged => _password.sink.add;
void submitForm() async {
try {
final Map user = {'email': _email.value, 'password': _password.value};
final jsonUser = json.encode(user);
_isLoading.sink.add(true);
// submit to server
final http.Response response = await http.post(
'http://192.168.1.213:5000/api/users/signin',
body: jsonUser,
headers: {'Content-Type': 'application/json'},
);
final Map<String, dynamic> decodedRes = await json.decode(response.body);
_isLoading.sink.add(false);
_loginSucceded.sink.add(true);
void dispose() {
_email.close();
_password.close();
_isLoading.close();
_loginSucceded.close();
}
} catch (e) {
print('error: $e');
_isLoading.sink.add(false);
}
}
}
Код виджета:
import 'package:flutter/material.dart';
import '../blocs/auth_bloc.dart';
class LoginPage extends StatelessWidget {
final authBloc = AuthBloc();
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: authBloc.loginSuccess,
builder: (context, snapshot1) {
return StreamBuilder(
stream: authBloc.isLoading,
builder: (context, snapshot2) {
print('loginSuccess? ${snapshot1.data} isLoading? ${snapshot2.data}');
return Scaffold(
body: !snapshot1.data && snapshot2.data
? _circularSpinner()
: snapshot1.data && snapshot2.data
? Navigator.pushReplacementNamed(context, '/dashboard')
: _buildPage());
},
);
},
);
}
Widget _buildPage() {
return Container(
margin: EdgeInsets.all(20.0),
child: Center(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
_emailField(authBloc),
_padding(),
_passwordField(authBloc),
_padding(),
_submitButton(authBloc)
],
),
),
),
);
}
Widget _circularSpinner() {
return Center(
child: CircularProgressIndicator(),
);
}
Widget _emailField(AuthBloc authBloc) {
return StreamBuilder(
stream: authBloc.email,
builder: (BuildContext context, snapshot) {
return TextField(
onChanged: authBloc.emailChanged,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: 'you@example.com',
labelText: 'Email Address',
errorText: snapshot.error,
border: OutlineInputBorder(),
),
);
},
);
}
Widget _passwordField(AuthBloc authBloc) {
return StreamBuilder(
stream: authBloc.password,
builder: (BuildContext context, snapshot) {
return TextField(
onChanged: authBloc.passwordChanged,
obscureText: true,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: '8 characters or more with at least 1 number',
labelText: 'Password',
errorText: snapshot.error,
border: OutlineInputBorder(),
),
);
},
);
}
Widget _padding() {
return Padding(
padding: EdgeInsets.only(top: 20.0),
);
}
Widget _submitButton(AuthBloc authBloc) {
return StreamBuilder(
stream: authBloc.submitValid,
builder: (context, snapshot) {
return RaisedButton(
child: Text('Login'),
color: Colors.blue,
onPressed: snapshot.hasError ? null : authBloc.submitForm,
);
});
}
}
main.dart
import 'package:flutter/material.dart';
import './app.dart';
import 'package:flutter/material.dart';
import './pages/auth.dart';
import './pages/dashboard.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/': (BuildContext context) => LoginPage(),
'/dashboard': (BuildContext context) => DashBoardPage(),
},
);
}
}
Вход
Restarted application in 1,462ms.
I/flutter ( 4998): loginSuccess? false isLoading? false
I/flutter ( 4998): loginSuccess? null isLoading? null
I/flutter ( 4998): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 4998): The following assertion was thrown building StreamBuilder<bool>(dirty, state:
I/flutter ( 4998): _StreamBuilderBaseState<bool, AsyncSnapshot<bool>>#34870):
I/flutter ( 4998): Failed assertion: boolean expression must not be null
I/flutter ( 4998):
I/flutter ( 4998): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter ( 4998): more information in this error message to help you determine and fix the underlying cause.
I/flutter ( 4998): In either case, please report this assertion by filing a bug on GitHub:
I/flutter ( 4998): https://github.com/flutter/flutter/issues/new?template=BUG.md
I/flutter ( 4998):
I/flutter ( 4998): When the exception was thrown, this was the stack:
I/flutter ( 4998): #0 LoginPage.build.<anonymous closure>.<anonymous closure>