2021.06.16 - [Usage/Flutter] - [Flutter] Bloc Pattern (stateful -> bloc -> skip event bloc )

 

[Flutter] Bloc Pattern (stateful -> bloc -> skip event bloc )

BLOC Bussiness Logic Component 비즈니스 로직과 ui를 구분 짓고 필요에 따라 원하는 부분만 데이터 처리 실 사용 예제 버튼 클릭시 증감 1. Stateful version ui widget - 버튼 클릭시 증감 //stateless widge..

mugon-devlog.tistory.com

Install package

https://pub.dev/packages/provider

Project 구조

main.dart
src
 - count_home.dart 
 - count_provider.dart
 - count_home_widget.dart // statelesswidget

main.dart

void main() {
  runApp(MyApp());
    // runApp에서 provider를 불러오면 MyApp에서 접근 가능
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MultiProvider(
        providers: [
          ChangeNotifierProvider(
              create: (BuildContext context) => CounterProvider())
        ],
                // child의 모든 하위 요소들은 CounterProvider에 접근 가능
        child: Home(),
      ),
      // home: ChangeNotifierProvider(
      //   create: (BuildContext context) => CounterProvider(),
      //   child: Home(),
      // ),
    );
  }

count_provider.dart

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

// ChangeNotifier 상속
// bloc 에서 stream 처리, sink 주입 했던 것을 처리해줌
class CounterProvider extends ChangeNotifier {
    // private
    // 이곳에서 선언되는 모든 변수는 상태관리 대상
  int _count = 0;
    // get 함수를 사용해서 private한 변수에 접근 가능하도록 만듦
  int get count => _count;
  add() {
    _count++;
        // 상태값이 update 되었다는 신호를 보내줌
    notifyListeners();
  }

  subtract() {
    _count--;
    notifyListeners();
  }
}

count_home.dart

class Home extends StatelessWidget {
  const Home({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
        // Provider.of로 사용할 provider를 가져옴
        // listen을 false로 해주면 현재의 widget은 rebuilding이 안됨
    CounterProvider countProvider =
        Provider.of<CounterProvider>(context, listen: false);
    return Scaffold(
      appBar: AppBar(
        title: Text("Provider"),
      ),
      body: CountView(),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () => countProvider.add(),
          ),
          IconButton(
            icon: Icon(Icons.remove),
            onPressed: () => countProvider.subtract(),
          ),
        ],
      ),
    );
  }

count_home_widget.dart

class CountView extends StatelessWidget {
  const CountView({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
                // Provider.of를 통해 접근해서 count값 가져옴
                // build 전체를 rebuilding 함
        Provider.of<CounterProvider>(context).count.toString(),
      ),
    );
  }
}

// 특정 위젯만 rebuilding
class CountView extends StatelessWidget {
  const CountView({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
     return Center(
            // consumer를 사용해서 특정 위젯만 rebuilding
       child: Consumer<CounterProvider>(
                builder: (context, provider, child) {
         return Text(
           provider.count.toString(),
         );
       }),
    );
  }
}

참고

https://www.youtube.com/watch?v=AmmjdvhQG1s 

 

728x90

'Study > Flutter' 카테고리의 다른 글

[Flutter] Factory 패턴  (0) 2021.07.13
[Flutter] Stateful widget  (0) 2021.06.14
[UI초급] Container, materialApp, Scaffold  (0) 2021.06.07

BLOC

Bussiness Logic Component

비즈니스 로직과 ui를 구분 짓고 필요에 따라 원하는 부분만 데이터 처리

실 사용 예제

버튼 클릭시 증감

1. Stateful version

ui widget - 버튼 클릭시 증감

//stateless widget으로 count를 보내 화면에 그려줌
body: CountViewStateless(count: count),
floatingActionButton: Row(
  mainAxisAlignment: MainAxisAlignment.end,
  children: [
    IconButton(
      icon: Icon(Icons.add),
      onPressed: () {
        setState(() {
          count++;
        });
      },
    ),
    IconButton(
      icon: Icon(Icons.remove),
      onPressed: () {
        setState(() {
          count--;
        });
      },
    ),
  ],
),

view - 결과 출력

import 'package:flutter/material.dart';

class CountViewStateless extends StatelessWidget {
  int count;
  CountViewStateless({Key key, this.count}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print("CountViewStateless Build !!");
    return Center(
        child: Text(
      count.toString(),
      style: TextStyle(fontSize: 80),
    ));
  }
}

2. bloc pattern

ui widget - 값을 변경시키는 이벤트 발생

// 전역변수로 bloc 생성
CountBloc countBloc;

class _BlocDisplayWidgetState extends State<BlocDisplayWidget> {
  @override
  void initState() {
    super.initState();
        // bloc
    countBloc = CountBloc();
  }

  @override
  void dispose() {
    super.dispose();
        // bloc 종료
    countBloc.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("bloc 패턴"),
      ),
      body: CountView(),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () {
                            // add는 이벤트를 등록한다는 의미
                            // 숫자를 더하거나 빼는 로직이 현재파일에는 존재하지 않음
              countBloc.countEventBloc.countEventSink.add(CountEvent.ADD_EVENT);
            },
          ),
          IconButton(
            icon: Icon(Icons.remove),
            onPressed: () {
              countBloc.countEventBloc.countEventSink
                  .add(CountEvent.SUBTRACT_EVENT);
            },
          ),
        ],
      ),
    );
  }
}

count_bloc - 비즈니스 로직 처리

import 'dart:async';

//bloc
class CountBloc {
  CountEventBloc countEventBloc = CountEventBloc();
  int _count = 0;
    // broadcast를 넣어주면 여러군데에서 구독 가능
  final StreamController<int> _countSubject = StreamController<int>.broadcast();

    // count는 _countSubject.stream를 구독하고 있는 모든 widget에게 변경된 상태값을 전달
  Stream<int> get count => _countSubject.stream;

    // 생성자
  CountBloc() {
        // countEventBloc의 _countEventSubject을 구독
    countEventBloc._countEventSubject.stream.listen(_countEventListen);
  }
    // 생성자에서 변화를 감지하여 listen으로 리턴 받은 것
    // 인자값 event에 해당하는 것은 enum에 등록된 이벤트 
  _countEventListen(CountEvent event) {
        // 비즈니스 로직 부분
    switch (event) {
      case CountEvent.ADD_EVENT:
        _count++;
        break;
      case CountEvent.SUBTRACT_EVENT:
        _count--;
        break;
    }
        // 결과값인 _count를 sink에 add로  넣어줌 
    _countSubject.sink.add(_count);
  }

  dispose() {
    _countSubject.close();
    countEventBloc.dispose();
  }
}

// event 
class CountEventBloc {
  final StreamController<CountEvent> _countEventSubject =
      StreamController<CountEvent>();
    // event 값이 sink로 들어오면 _countEventSubject를 통해 구독자에게 알려줌  
  Sink<CountEvent> get countEventSink => _countEventSubject.sink;

  dispose() {
    _countEventSubject.close();
  }
}

// event에 해당하는 enum
enum CountEvent { ADD_EVENT, SUBTRACT_EVENT }

count_view 결과값 받아오는 곳

StreamBuilder(
    // stream을 리스닝 
  stream: countBloc.count, // Stream<int> get count => _countSubject.stream; 구독
  initialData: 0,
    // snapshot으로 값이 들어옴
  builder: (BuildContext context, AsyncSnapshot<int> snapshot) { 
    if (snapshot.hasData) {
      return Text(
        snapshot.data.toString(),
        style: TextStyle(fontSize: 80),
      );
    }
    return CircularProgressIndicator();
  },
),

3. Skip event bloc pattern

ui 위와 다르게 바로 이벤트 호출함

class _BlocDisplayWidgetState extends State<BlocDisplayWidget> {
  @override
  void initState() {
    super.initState();
    countBloc = CountBloc();
  }

  @override
  void dispose() {
    super.dispose();
    countBloc.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("bloc 패턴"),
      ),
      body: CountView(),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () {
                            // 이벤트 호출
              countBloc.add();
            },
          ),
          IconButton(
            icon: Icon(Icons.remove),
            onPressed: () {
              countBloc.subtract();
            },
          ),
        ],
      ),
    );
  }
}

count_bloc - skip event 비즈니스 로직 처리

import 'dart:async';

class CountBloc {
  int _count = 0;
  final StreamController<int> _countSubject = StreamController<int>.broadcast();
  Stream<int> get count => _countSubject.stream;

  add() {
    _count++;
    _countSubject.sink.add(_count);
  }

  subtract() {
    _count--;
    _countSubject.sink.add(_count);
  }

  dispose() {
    _countSubject.close();
  }
}

참고

https://www.youtube.com/watch?v=AY6i0a4BM7o 

 

728x90

+ Recent posts