본문 바로가기

Language/Dart

Flutter BLoC, RxDart combineLatestStream BehaviorSubject On Submit

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

'Language > Dart' 카테고리의 다른 글

Flutter - FutureBuilder  (0) 2020.09.22
Flutter Animation  (0) 2020.09.21
Flutter BLoC, RxDart combineLatestStream BehaviorSubject On Submit  (0) 2020.09.18
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