我有一个简单的应用程序可以处理登录,这只是了解 Bloc 的工作原理以及如何管理状态的练习。
我遇到了 bloc 问题,由于某种原因,在添加事件后,它不会重建小部件树。
我有 3 个区块:
Authentication
应该管理当前用户的状态并检查它是否存在令牌并显示 home
。
Login
应管理登录页面并在用户尝试登录并将其重定向到主页时做出响应。
Registration
应管理注册过程并将用户重定向到主页。
登录有效,我可以模拟登录并在用户重定向到主页后。
是注册的问题,注册后UI不更新,老是看注册表。
main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'bloc/registration/registration_bloc.dart';
import 'screens/home/home_screen.dart';
import 'screens/login/login_screen.dart';
import 'repositories/interfaces/authentication.dart';
import 'repositories/rest/authentication.dart';
import 'bloc/authentication/authentication_bloc.dart';
import 'routes.dart';
import 'theme.dart';
void main() {
runApp(
MultiRepositoryProvider(
providers: [
RepositoryProvider<AuthenticationRepository>(
create: (context) {
return RestAuthenticationProvider();
},
),
],
child: MultiBlocProvider(
providers: [
BlocProvider<AuthenticationBloc>(
create: (context) {
final authService = context.read<AuthenticationRepository>();
return AuthenticationBloc(authService)..add(AppLoaded());
},
),
],
child: MyApp(),
),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: themeData,
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationAuthenticated) {
return HomeScreen();
}
return LoginScreen();
},
),
onGenerateRoute: RouteGenerator.generateRoute,
);
}
}
routes.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'screens/home/home_screen.dart';
import 'screens/login/login_screen.dart';
import 'screens/register/register_screen.dart';
class RouteGenerator {
static const String loginScreen = '/login';
static const String registerScreen = '/register';
static const String homeScreen = '/home';
RouteGenerator._();
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case loginScreen:
{
return MaterialPageRoute(
builder: (_) {
return LoginScreen();
},
);
}
case registerScreen:
{
return MaterialPageRoute(
builder: (_) {
return RegisterScreen();
},
);
}
case homeScreen:
{
return MaterialPageRoute(
builder: (_) {
return HomeScreen();
},
);
}
/* case singlePermissionPage:
{
final SinglePermissionRequestArguments permissionRequestArguments =
settings.arguments;
return MaterialPageRoute(
builder: (_) {
return SinglePermissionRequestPage(
permissionRequest: permissionRequestArguments.pm,
);
},
);
} */
default:
throw FormatException('Route not found');
}
}
}
home_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../bloc/authentication/authentication_bloc.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final authBloc = BlocProvider.of<AuthenticationBloc>(context);
return Scaffold(
appBar: AppBar(
title: Text('Home'),
actions: [
IconButton(
icon: Icon(Icons.logout),
onPressed: () {
authBloc.add(UserLoggedOut());
},
)
],
),
body: Center(
child: Text('Home'),
),
);
}
}
登录.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../bloc/authentication/authentication_bloc.dart';
import '../../bloc/login/login_bloc.dart';
import '../../repositories/interfaces/authentication.dart';
import 'components/loading_login.dart';
import 'components/form_login.dart';
class LoginScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login'),
),
body: SafeArea(
minimum: const EdgeInsets.all(16),
child: _AuthForm(),
),
);
}
}
class _AuthForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AuthenticationRepository authService =
context.read<AuthenticationRepository>();
final AuthenticationBloc authBloc = context.read<AuthenticationBloc>();
return SingleChildScrollView(
child: BlocProvider<LoginBloc>(
create: (context) => LoginBloc(authBloc, authService),
child: SignInForm(),
),
);
}
}
登录表单
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'loading_login.dart';
import '../../../bloc/login/login_bloc.dart';
import '../../../validation.dart';
import '../../../routes.dart';
class SignInForm extends StatefulWidget {
@override
_SignInFormState createState() => _SignInFormState();
}
class _SignInFormState extends State<SignInForm> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final _passwordController = TextEditingController();
final _emailController = TextEditingController();
@override
Widget build(BuildContext context) {
return BlocListener<LoginBloc, LoginState>(
listener: (context, state) {
if (state is LoginFailure) {
_showError(state.error);
}
},
child: BlocBuilder<LoginBloc, LoginState>(
builder: (context, state) {
if (state is LoginLoading) {
return LoadingLogin();
}
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
child: Form(
key: _formKey,
autovalidateMode: AutovalidateMode.always,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
autocorrect: false,
validator: (value) => validateEmailAddress(value),
decoration: InputDecoration(
labelText: 'Indirizzo email',
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () {
_emailController.clear();
},
),
),
),
SizedBox(
height: 12,
),
TextFormField(
obscureText: true,
controller: _passwordController,
validator: (value) => validatePassword(value),
decoration: InputDecoration(
labelText: 'Password',
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () {
_passwordController.clear();
},
),
),
),
SizedBox(
height: 20.0,
),
RaisedButton(
padding: const EdgeInsets.all(16),
child: Text('LOG IN'),
onPressed: () => _formKey.currentState.validate()
? onLoginButtonPressed(context)
: null,
),
SizedBox(
height: 10.0,
),
FlatButton(
child: Text('Registrati'),
onPressed: () => Navigator.pushReplacementNamed(
context,
RouteGenerator.registerScreen,
),
),
],
),
),
),
],
);
},
),
);
}
void onLoginButtonPressed(BuildContext context) {
context.read<LoginBloc>().add(
LoginInWithEmailButtonPressed(
email: _emailController.text,
password: _passwordController.text,
),
);
}
void _showError(String error) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(error),
backgroundColor: Theme.of(context).errorColor,
),
);
}
}
register_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../bloc/authentication/authentication_bloc.dart';
import '../../bloc/registration/registration_bloc.dart';
import '../../repositories/interfaces/authentication.dart';
import '../../screens/home/home_screen.dart';
import '../../screens/register/components/loading_registration.dart';
import 'components/form_registration.dart';
class RegisterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Registrati'),
),
body: SafeArea(
minimum: const EdgeInsets.all(16),
child: _FormRegistration(),
),
);
}
}
class _FormRegistration extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AuthenticationRepository authService =
context.read<AuthenticationRepository>();
final AuthenticationBloc authBloc = context.read<AuthenticationBloc>();
return SingleChildScrollView(
child: BlocProvider<RegistrationBloc>(
create: (context) => RegistrationBloc(authBloc, authService),
child: FormRegistration(),
),
);
}
}
表格注册
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:login/bloc/authentication/authentication_bloc.dart';
import '../../../routes.dart';
import 'loading_registration.dart';
import '../../../models/user.dart';
import '../../../screens/home/home_screen.dart';
import '../../../validation.dart';
import '../../../bloc/registration/registration_bloc.dart';
class FormRegistration extends StatefulWidget {
@override
_FormRegistrationState createState() => _FormRegistrationState();
}
class _FormRegistrationState extends State<FormRegistration> {
final GlobalKey<FormState> _registrationFormKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _firstNameController = TextEditingController();
final _lastNameController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
@override
Widget build(BuildContext context) {
return BlocListener<RegistrationBloc, RegistrationState>(
listener: (context, state) {
if (state is RegistrationFailure) {
_showError(state.error);
}
if (state is RegistrationSuccess) {
context.read<AuthenticationBloc>().add(
UserLoggedIn(
user: User(
email: '',
firstName: '',
lastName: '',
),
),
);
}
},
child: BlocBuilder<RegistrationBloc, RegistrationState>(
builder: (context, state) {
if (state is RegistrationLoading) {
return LoadingRegistration();
}
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
height: 5,
),
Container(
child: Form(
key: _registrationFormKey,
autovalidateMode: AutovalidateMode.always,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextFormField(
controller: _firstNameController,
keyboardType: TextInputType.emailAddress,
autocorrect: false,
validator: (value) => validateFirstName(value),
decoration: InputDecoration(
labelText: 'Nome',
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () {
_firstNameController.clear();
},
),
),
),
TextFormField(
controller: _lastNameController,
keyboardType: TextInputType.emailAddress,
autocorrect: false,
validator: (value) => validateLastName(value),
decoration: InputDecoration(
labelText: 'Cognome',
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () {
_lastNameController.clear();
},
),
),
),
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
autocorrect: false,
validator: (value) => validateEmailAddress(value),
decoration: InputDecoration(
labelText: 'Indirizzo email',
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () {
_emailController.clear();
},
),
),
),
TextFormField(
obscureText: true,
controller: _passwordController,
validator: (value) => validatePassword(value),
decoration: InputDecoration(
labelText: 'Password',
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () {
_passwordController.clear();
},
),
),
),
TextFormField(
obscureText: true,
controller: _confirmPasswordController,
validator: (value) => validateConfirmPassword(
value,
_passwordController.text,
),
decoration: InputDecoration(
labelText: 'Conferma Password',
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () {
_confirmPasswordController.clear();
},
),
),
),
SizedBox(
height: 20.0,
),
RaisedButton(
padding: const EdgeInsets.all(16),
child: Text('Registrati'),
onPressed: () =>
_registrationFormKey.currentState.validate()
? onRegisterButtonPressed(context)
: null,
),
FlatButton(
padding: const EdgeInsets.all(16),
child: Text('Torna al login'),
onPressed: () => Navigator.pushNamed(
context,
RouteGenerator.loginScreen,
),
),
],
),
),
),
],
);
},
),
);
}
void onRegisterButtonPressed(BuildContext context) {
final User user = new User(
firstName: _firstNameController.text,
lastName: _lastNameController.text,
email: _emailController.text,
password: _passwordController.text,
);
context.read<RegistrationBloc>().add(
RegisterButtonPressed(
user: user,
),
);
}
void _showError(String error) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(error),
backgroundColor: Theme.of(context).errorColor,
),
);
}
}
登录区块
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
import 'package:equatable/equatable.dart';
import '../authentication/authentication_bloc.dart';
import '../../models/user.dart';
import '../../exceptions/authentication_exception.dart';
import '../../repositories/interfaces/authentication.dart';
part 'login_event.dart';
part 'login_state.dart';
class LoginBloc extends Bloc<LoginEvent, LoginState> {
final AuthenticationBloc _authenticationBloc;
final AuthenticationRepository _authenticationRepository;
LoginBloc(
AuthenticationBloc authenticationBloc,
AuthenticationRepository authenticationRepository,
) : assert(authenticationBloc != null),
assert(authenticationRepository != null),
_authenticationBloc = authenticationBloc,
_authenticationRepository = authenticationRepository,
super(null);
@override
LoginState get initialState => LoginInitial();
@override
Stream<LoginState> mapEventToState(LoginEvent event) async* {
if (event is LoginInWithEmailButtonPressed) {
yield* _mapLoginWithEmailToState(event);
}
}
Stream<LoginState> _mapLoginWithEmailToState(
LoginInWithEmailButtonPressed event,
) async* {
yield LoginLoading();
try {
final User user =
await _authenticationRepository.signInWithEmailAndPassword(
event.email,
event.password,
);
if (user is User) {
// push new authentication event
_authenticationBloc.add(
UserLoggedIn(
user: user,
),
);
yield LoginSuccess();
yield LoginInitial();
} else {
yield LoginFailure(
error: 'Something very weird just happened',
);
}
} on AuthenticationException catch (e) {
yield LoginFailure(
error: 'I dati di login non sono corretti.',
);
} catch (e) {
yield LoginFailure(
error: 'Si è verificato un errore sconosciuto, riprova più tardi.',
);
}
}
}
注册区块
import 'dart:async';
import 'package:login/exceptions/registration_exception.dart';
import 'package:meta/meta.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import '../../repositories/interfaces/authentication.dart';
import '../../models/user.dart';
import '../../bloc/authentication/authentication_bloc.dart';
part 'registration_event.dart';
part 'registration_state.dart';
class RegistrationBloc extends Bloc<RegistrationEvent, RegistrationState> {
final AuthenticationBloc _authenticationBloc;
final AuthenticationRepository _authenticationRepository;
RegistrationBloc(
AuthenticationBloc authenticationBloc,
AuthenticationRepository authenticationRepository,
) : assert(authenticationBloc != null),
assert(authenticationRepository != null),
_authenticationBloc = authenticationBloc,
_authenticationRepository = authenticationRepository,
super(null);
@override
RegistrationState get initialState => RegistrationInitial();
@override
Stream<RegistrationState> mapEventToState(
RegistrationEvent event,
) async* {
if (event is RegisterButtonPressed) {
yield RegistrationLoading();
try {
final bool registered =
await _authenticationRepository.registerUser(event.user);
if (registered) {
yield RegistrationSuccess();
yield RegistrationInitial();
} else {
yield RegistrationFailure(
error: 'Non è stato possibile registrarti.',
);
}
} on RegistrationException catch (e) {
yield RegistrationFailure(
error: 'Errore nella registrazione',
);
} catch (e) {
yield RegistrationFailure(
error: 'Si è verificato un errore sconosciuto.',
);
}
}
}
}
身份验证块
import 'package:meta/meta.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import '../../repositories/interfaces/authentication.dart';
import '../../models/user.dart';
part 'authentication_event.dart';
part 'authentication_state.dart';
class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationState> {
final AuthenticationRepository _authenticationService;
AuthenticationBloc(AuthenticationRepository authenticationService)
: assert(authenticationService != null),
_authenticationService = authenticationService,
super(null);
@override
AuthenticationState get initialState => AuthenticationInitial();
@override
Stream<AuthenticationState> mapEventToState(
AuthenticationEvent event) async* {
if (event is AppLoaded) {
yield* _mapAppLoadedToState(event);
}
if (event is UserLoggedIn) {
yield* _mapUserLoggedInToState(event);
}
if (event is UserLoggedOut) {
yield* _mapUserLoggedOutToState(event);
}
}
Stream<AuthenticationState> _mapAppLoadedToState(AppLoaded event) async* {
yield AuthenticationLoading(); // Display splash screen
try {
final _currentUser = await _authenticationService.getCurrentUser();
if (_currentUser != null) {
yield AuthenticationAuthenticated(
user: _currentUser,
);
} else {
yield AuthenticationNotAuthenticated(); // Show login form
}
} catch (e) {
yield AuthenticationFailure(message: e.message ?? "An error occured");
}
}
Stream<AuthenticationState> _mapUserLoggedInToState(
UserLoggedIn event,
) async* {
yield AuthenticationAuthenticated(
user: event.user,
);
}
Stream<AuthenticationState> _mapUserLoggedOutToState(
UserLoggedOut event,
) async* {
await _authenticationService.signOut();
yield AuthenticationNotAuthenticated();
}
}
如上所述,登录就像一个魅力,注册屏幕只是不要在家里重定向我。
我曾尝试使用调试器,似乎在我将事件添加到 AuthBloc 后,它会重新评估注册构建器并重新构建表单,而不是返回主文件并评估 authBloc。