본문 바로가기
📱Mobile/🔥Flutter

[Flutter] 기초가 되는 StreamBuilder BLOC 패턴, StreamSubscription, yield example

by 후누스 토르발즈 2021. 1. 6.
반응형

https://pub.dev/packages/flutter_bloc
https://pub.dev/packages/flutter_bloc

 

저번에는 Cubit를 사용하여 BLOC 패턴을 제작해보았다. 훤하게 눈에 잘 보이는 Cubit로 필요 동작만 깔끔하게 옵저빙 했지만 역시나 BLOC는 복잡하다. 이번에 작성하는 StreamBuilder를 사용한 BLOC 패턴 또한 복잡하다. 결국 하나의 기능을 만들려면 클래스를 최소 3, 4개를 만들어야하니 문제이다.

 

 

 

파일트리이다.

 

로직을 나누고, 이벤트와 상태를 관리한다.

 

main.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_bloc_with_stream/ticker/ticker.dart';

import 'bloc/ticker_bloc.dart';

void main() {
  runApp(TickerApp());
}

class TickerApp extends MaterialApp {
  TickerApp({Key key})
      : super(
          key: key,
          home: BlocProvider(
            create: (_) => TickerBloc(Ticker()),
            child: TickerPage(),
          ),
        );
}

class TickerPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Bloc with Streams'),
      ),
      body: BlocBuilder<TickerBloc, TickerState>(
        builder: (context, state) {
          if (state is TickerTickSuccess) {
            return Center(
              child: Text('Tick #${state.count}'),
            );
          }
          return const Center(
            child: Text('Press the floating button to start'),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.read<TickerBloc>().add(TickerStarted()),
        tooltip: 'Start',
        child: const Icon(Icons.timer),
      ),
    );
  }
}

 

ticker_bloc.dart

import 'dart:async';
import 'package:bloc/bloc.dart';
import '../ticker/ticker.dart';

part 'ticker_event.dart';
part 'ticker_state.dart';

class TickerBloc extends Bloc<TickerEvent, TickerState> {
  TickerBloc(this._ticker) : super(TickerInitial());

  final Ticker _ticker;
  StreamSubscription _subscription;

  @override
  Stream<TickerState> mapEventToState(TickerEvent event) async* {
    if (event is TickerStarted) {
      print('event is TickerStarted');
      await _subscription?.cancel();
      _subscription = _ticker.tick().listen((tick) => add(_TickerTicked(tick)));
    }
    if (event is _TickerTicked) {
      print('event is _TickerTicked');
      yield TickerTickSuccess(event.tickCount);
    }
  }

  @override
  Future<void> close() {
    print('close()');
    _subscription?.cancel();
    return super.close();
  }
}

 

ticker_event.dart

part of 'ticker_bloc.dart';

abstract class TickerEvent {
  const TickerEvent();
}

class TickerStarted extends TickerEvent {}

class _TickerTicked extends TickerEvent {
  const _TickerTicked(this.tickCount);

  final int tickCount;
}

 

 

ticker_state.dart

part of 'ticker_bloc.dart';
abstract class TickerState {
  const TickerState();

}

class TickerInitial extends TickerState {}

class TickerTickSuccess extends TickerState {
  const TickerTickSuccess(this.count);

  final int count;
}

 

 

 

ticker.dart

class Ticker {
  Stream<int> tick() {
    return Stream.periodic(const Duration(seconds: 1), (x) => x).take(10);
  }
}

 

 

 

 

완성된 실행화면이다.

 

 

 

main.dart

처음 BlocProvider를 설정한다. 이전에는 build 였던거같은데 create 로 변경된거같다.

 

 

 

 

main.dart

BlocBuilder를 생성하는데 TickerBloc와 TickerState다.

Bloc 로직을 받고 State를 받는것인데 Bloc는 로직을 실행하려고 받는것이라 보면되고 State는 변한 상태를 받기위해서 받는다.

 

 

 

 

main.dart

플로팅액션버튼 클릭시 context.read<TickerBloc>() 에 add(TickerStarted()) 하게 되는데

 

 

ticker_bloc.dart

여기서 받는다. add(TickerStarted()) 이니 event is TickerStarted에 걸쳐서 true면 이전에 실행했을지도 모르는 subscription을 끊고 새로운 tick().listen으로 add(_TickerTicked(tick))) 한다. 그러하게되면 

 

 

ticker.dart

여기서 1초마다 take(10) 까지 Stream<int> return 한다.

그럼 위쪽에서 구독을 걸었으니 다시 mapEventToState() 로 이벤트가 들어온다.

 

 

 

ticker_bloc.dart

이제는 계속 take(10) 이니까 int 0 부터 9 까지 이쪽으로 들어올것이다.

여기서 값을 yield 한다.

 

위쪽에보면 

ticker_bloc.dart

 

async* 가 있는데 이것이 있어야만 yield 할수있다. yield가 생소할텐데

 

return과 비슷하지만 뒤의함수가 계속 실행되는것이 yield이다.

미끄럼틀이 있는데 yield를 써서 미끄럼틀로 내려보내도 다음 탑승자가 있을 수 있으니 일부러 함수를 끝내지 않는다고 보내면 된다.

하지만 return의 경우 그냥 미끄럼틀을 문닫고 끝내는 것이다.

 

 

 

 

 

main.dart

 여기서 상태를 받아서 처리하게된다. 들어온 파라미터들의 상태가 TickerTickSuccess로 반환되면 state.count 값을 그린다.

 

 

 

편하다면 편하지만 역시 BLOC는 복잡성 문제가 있다. 클래스가 너무 많다..

 

중규모 프로젝트에서 쓸 수 있는 Provider패턴도 꼭 사용해보자!

 

 

 

 

반응형