lang/dart

Flutter BLoC, RxDart combineLatestStream BehaviorSubject On Submit

C/H 2020. 9. 18. 08:30

Flutter BLoC, RxDart combineLatestStream BehaviorSubject

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.3

  rxdart: ^0.24.1
  #rxdar: ^0.16.7

main.dart

import 'package:flutter/material.dart';
import 'src/app.dart';

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

src/app.dart

import 'package:flutter/material.dart';
import 'screens/login_screen.dart';
import 'blocs/provider.dart';

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider(
      child: MaterialApp(
        title: 'Login',
        home: Scaffold(
          appBar: AppBar(
            title: Text('LoginForm'),
          ),
          body: LoginScreen(),
        ),
      ),
    );
  }
}

src/screens/login_screen.dart

import 'package:flutter/material.dart';
import '../blocs/bloc.dart';
import '../blocs/provider.dart';

class LoginScreen extends StatefulWidget {
  @override
  _LoginScreenState createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {

  @override
  Widget build(BuildContext context) {
    final bloc = Provider.of(context);

    return Container(
      margin: EdgeInsets.all(8.0),
      child: Form(child: 
        Column(children: <Widget>[
          fieldEmail(bloc),
          fieldPassword(bloc),
          _submit(bloc),
          ],
        ),
      ),
    );
  }

  Widget fieldEmail(Bloc bloc) {
    return StreamBuilder(
      stream: bloc.email,
      builder: (context, snapshot) {
        return TextField(
          onChanged: bloc.changeEmail,
          keyboardType: TextInputType.emailAddress,
          decoration: InputDecoration(
            labelText: 'Email',
            hintText: 'useanem@domain.com',
            errorText: snapshot.error,
          ),
        );
      }
    );
  }

  Widget fieldPassword(Bloc bloc) {
    return StreamBuilder(
      stream: bloc.password,
      builder: (context, snapshot) {
        return TextField(
          onChanged: bloc.changePassword,
          obscureText: true,
          decoration: InputDecoration(
            labelText: 'Password',
            hintText: 'over 3 charactor',
            errorText: snapshot.error,
          ),
        );
      }
    );
  }

  Widget _submit(Bloc bloc) {
    return StreamBuilder(
      stream: bloc.submitValid,
      builder: (context, snapshot) {
        return RaisedButton(
          color: Colors.blue,
          child: Text('Submit', style: TextStyle(color: Colors.white),),
          onPressed: snapshot.hasError ? null : bloc.submit,
        );
      }
    );
  }
}

src/blocs/bloc.dart

import 'dart:async';
import 'package:flutter/foundation.dart';

import 'validators.dart';
import 'package:rxdart/rxdart.dart';

class Bloc extends Object with Validators {
  // https://pub.dev/documentation/rxdart/latest/rx/BehaviorSubject-class.html
  final _email = BehaviorSubject<String>();
  final _passwd = BehaviorSubject<String>();

  Stream<String> get email => _email.stream.transform(validatorEmail);
  Stream<String> get password => _passwd.stream.transform(validatorPassword);
  // https://pub.dev/documentation/rxdart/latest/rx/CombineLatestStream-class.html
  // https://pub.dev/documentation/rxdart/latest/rx/CombineLatestStream/combine2.html
  // Stream<bool> get submitValid => Observable.combineLatest2(email, password, (e, p)=>e&&p);
  Stream<bool> get submitValid => CombineLatestStream([email, password], (_)=>true);

  Function(String) get changeEmail => _email.sink.add;
  Function(String) get changePassword => _passwd.sink.add;

  submit() {
    final validEmail = _email.value;
    final validPassword = _passwd.value;

    print('Email is $validEmail');
    print('Password is $validPassword');
  }

  dispose() {
    _email.close();
    _passwd.close();
  }
}

src/blocs/provider.dart

import 'package:flutter/material.dart';
import 'bloc.dart';

class Provider extends InheritedWidget {
  final bloc = Bloc();

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

  // @override
  // bool updateShuldNotify(_) => true;
  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => true;
  /*{
    throw UnimplementedError();
  }*/

  static Bloc of(BuildContext context) {
    // ignore: deprecated_member_use
    // return (context.inheritFromWidgetOfExactType(Provider) as Provider).bloc;
    // https://stackoverflow.com/a/59304297
    return (context.dependOnInheritedWidgetOfExactType<Provider>()).bloc;

  }
}

src/blocs/validators.dart

import 'dart:async';

class Validators {
  final validatorEmail = StreamTransformer<String, String>.fromHandlers(
    handleData: (String email, sink) {
      if( email.contains('@') ){
        sink.add(email);
      } else {
        sink.addError('Enter a valid email');
      }
    }
  );

  final validatorPassword = StreamTransformer<String, String>.fromHandlers(
    handleData: (String passwd, sink) {
      if( passwd.length >= 3 ) {
        sink.add(passwd);
      } else {
        sink.addError('password must be at leat 3 characters');
      }
    }
  );
}

Flutter State

반응형

'lang > dart' 카테고리의 다른 글

Flutter - FutureBuilder  (0) 2020.09.22
Flutter Animation  (0) 2020.09.21
Flutter BLoC, RxDart combineLatestStream  (0) 2020.09.17
Flutter BLoC, Scoped Instances  (0) 2020.09.16
Flutter BLoC, Single Global Instance  (0) 2020.09.15