Flutter Auth (шаблон BLoC и rxDart) - PullRequest
       23

Flutter Auth (шаблон BLoC и rxDart)

0 голосов
/ 26 февраля 2019

Я хотел создать приложения с авторизацией на партнере BLoC, но обнаружил ошибку:

The following NoSuchMethodError was thrown building AuhtScreen(dirty, state: _AuhtScreenState<dynamic>#00539):
The getter 'blocState' was called on null.
Receiver: null
Tried calling: blocState

Она вызывается при следующих обстоятельствах:

В AuhtScreen

AuthBloc authBloc = BlocProvider.of(context).authBloc; (context = StatefulElement)

В BlocProvider

static BlocState of(BuildContext context) { (context = StatefulElement)
    return (context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider).blocState; (context = StatefulElement)(NULL)
  }

Я не понимаю, почему это не работает, я все делаю правильно, может я что-то пропустил или не понял ... Помогите решить проблему!

Весь код:

AuthBloc

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:mifity/models/auht_detail.dart';
import 'package:mifity/models/user.dart';
import 'package:mifity/screens/main_screen.dart';
import 'package:mifity/services/auth_service.dart';
import 'package:rxdart/rxdart.dart';

class AuthBloc {
  AuthService authService;
  BuildContext _context;

  final currentUserSubject = BehaviorSubject<User>.seeded(null);
  final emailSubject = BehaviorSubject<String>.seeded('');
  final passwordSubject = BehaviorSubject<String>.seeded('');
  final loadingSubject = BehaviorSubject<bool>.seeded(false);
  final loginSubject = BehaviorSubject<Null>.seeded(null);

  //sink
  void Function(String) get emailChanged => emailSubject.sink.add;
  void Function(String) get passwordChanged => passwordSubject.sink.add;
  void Function(BuildContext) get submitLogin => (context) {
        this.setContext(context);
        loginSubject.add(null);
      };

  //stream
  Stream<User> get currentUser => currentUserSubject.stream;
  Stream<String> get emailStream => emailSubject.stream;
  Stream<String> get passwordStream => passwordSubject.stream;
  Stream<bool> get loading => loadingSubject.stream;

  AuthBloc({this.authService}) {
    Stream<AuhtDetail> auhtDetailStream = Observable.combineLatest2(
        emailStream, passwordStream, (email, password) {
      return AuhtDetail(email: email, password: password);
    });
    Stream<User> loggedIn = Observable(loginSubject.stream)
        .withLatestFrom(auhtDetailStream, (_, auhtDetail) {
      return auhtDetail;
    }).flatMap((auhtDetail) {
      return Observable.fromFuture(authService.loginUser(auhtDetail))
          .doOnListen(() {
        loadingSubject.add(true);
      }).doOnDone(() {
        loadingSubject.add(false);
      });
    });

    loggedIn.listen((User user) {
      currentUserSubject.add(user);
      Navigator.push(
        _context,
        new MaterialPageRoute(builder: (context) => MainScreen()),
      );
    }, onError: (error) {
      Scaffold.of(_context).showSnackBar(new SnackBar(
        content: new Text("Username or password incorrect"),
      ));
    });
  }

  setContext(BuildContext context) {
    _context = context;
  }

  close() {
    emailSubject.close();
    passwordSubject.close();
    loadingSubject.close();
    loginSubject.close();
  }
}

BlocProvider

import 'package:flutter/material.dart';
import 'package:mifity/blocs/auth_bloc.dart';
import 'package:mifity/services/auth_service.dart';

class BlocProvider extends InheritedWidget {
  final blocState = new BlocState(
    authBloc: AuthBloc(authService: AuthService()),
  );

  BlocProvider({Key key, Widget child}) : super(key: key, child: child);

  bool updateShouldNotify(_) => true;

  static BlocState of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider)
        .blocState;
  }
}

class BlocState {
  final AuthBloc authBloc;
  BlocState({this.authBloc});
}

AuthService

import 'dart:async';

import 'package:mifity/models/auht_detail.dart';
import 'package:mifity/models/error.dart';
import 'package:mifity/models/user.dart';

class AuthService {
  Future<User> loginUser(AuhtDetail detail) async {
    await Future.delayed(Duration(seconds: 1)); //simulate network delay
    if (detail.email == 'johndoe@acme.com' && detail.password == '1234') {
      return User(
          id: 1,
          name: 'John Doe',
          email: 'johndoe@acme.com',
          age: 26,
          profilePic: 'john_doe.png');
    } else {
      throw ClientError(message: 'login details incorrect.');
    }
  }
}

Валидатор:

class Validator {
  String validateEmail(String value) {
    if (value.isEmpty) return 'Email Should not be empty';
    final RegExp emailRegEx = new RegExp(r'^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$');
    if (!emailRegEx.hasMatch(value)) return 'Your Email is invalid';
    return null;
  }

  String validatePassword(String value) {
    if (value.length < 4) return 'Password should be four characters or more';
    return null;
  }
}

AuhtScreen

import 'package:flutter/material.dart';
import 'package:mifity/blocs/auth_bloc.dart';
import 'package:mifity/blocs/bloc_provider.dart';
import 'package:mifity/helpers/validators.dart';

class AuhtScreen extends StatefulWidget {
  @override
  _AuhtScreenState createState() => _AuhtScreenState();
}

class _AuhtScreenState<StateClass> extends State<AuhtScreen> {
  TextEditingController emailController = TextEditingController();
  TextEditingController passwordController = TextEditingController();
  Validator validator = new Validator();
  final formKey = GlobalKey<FormState>();
  DecorationImage backgroundImage = new DecorationImage(
    image: new ExactAssetImage('assets/images/bg_image.jpg'),
    fit: BoxFit.cover,
  );

  @override
  Widget build(BuildContext context) {
    AuthBloc authBloc = BlocProvider.of(context).authBloc;
    final Size screenSize = MediaQuery.of(context).size;

    return Scaffold(
        appBar: AppBar(
          title: Text('Login'),
        ),
        body: Builder(builder: (context) {
          return SingleChildScrollView(
            child: Container(
              height: screenSize.height - AppBar().preferredSize.height,
              padding: EdgeInsets.all(10.0),
              alignment: Alignment.center,
              decoration: BoxDecoration(
                  image: (backgroundImage != null) ? backgroundImage : null),
              child: Center(
                child: Form(
                  key: formKey,
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      TextFormField(
                        style: TextStyle(color: Colors.white),
                        controller: emailController,
                        decoration: InputDecoration(
                            labelText: 'email',
                            labelStyle: TextStyle(color: Colors.grey)),
                        validator: validator.validateEmail,
                      ),
                      TextFormField(
                        style: TextStyle(color: Colors.white),
                        controller: passwordController,
                        decoration: InputDecoration(
                            labelText: 'password',
                            labelStyle: TextStyle(color: Colors.grey)),
                        obscureText: true,
                        validator: validator.validatePassword,
                      ),
                      SizedBox(
                        height: 20.0,
                      ),
                      StreamBuilder<bool>(
                        initialData: false,
                        stream: authBloc.loading,
                        builder: (context, loadingSnapshot) {
                          return SizedBox(
                            width: double.infinity,
                            child: RaisedButton(
                              color: Colors.deepOrange,
                              textColor: Colors.white,
                              child: Text((loadingSnapshot.data)
                                  ? 'Login ...'
                                  : 'Login'),
                              onPressed: () {
                                _submit(context, authBloc);
                              },
                            ),
                          );
                        },
                      ),
                    ],
                  ),
                ),
              ),
            ),
          );
        }));
  }

  _submit(context, AuthBloc authBloc) {
    authBloc.emailChanged(emailController.text);
    authBloc.passwordChanged(passwordController.text);

    if (formKey.currentState.validate()) {
      authBloc.submitLogin(context);
    }
  }
}

1 Ответ

0 голосов
/ 27 февраля 2019

Я идиот!)

MyApp:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
        child: MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new AuhtScreen(),
    ));
  }
}
...