Notice
Recent Posts
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- 이직
- 크레인 인형뽑기
- flutter_secure_storage
- 주변에는 능력자 뿐이야!!
- dfs
- 댓글이 하나도 없오...ㅠㅠ
- bloc
- 나도 코딩 잘할래!!!!!!!!!!!
- FutureBuilder
- 쒸익!!!!!!!!!
- flutter_local_notification
- open weather api
- network
- 코딩 잘하고 싶어!!
- Null Safety
- flutter
- Flutter2.8
- 플러터 책
- 누가 보기는 하는걸까...ㅠㅠ
- hero animation
- 다트 책
- 주니어개발자
- 프로그래머스
- TODO
- flutter secure storage
- 다트&플러터
- 플러터
- flutter-layout
- 포?코DX
- 편하다요
Archives
- Today
- Total
오늘하루도 우힣ㅎ
Flutter) BLoC으로 counter 만들기 본문
앞의 포스팅에서는 BLoC에 대한 정보를 알아 보았다. 이 포스팅에서는 플러터 프로젝트 처음생성시 만들어지는 couter를 BLoC으로 만들어보려 한다.
1. 파일 구조
|
2. main.dart 분석
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}//setSate를 통하여 counter가 늘어나는것을 제어 하도록 구현이 되어 있다.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,//버튼을 누르게 되면 _incrementCounter함수가 실행이 되게 된다.
tooltip: 'Increment',
child: Icon(Icons.add),//현재는 애드를 위한 버튼 만이 존재한다.
),
);
}
}
- main.dart 페이지에서는 현재 setSate()로 state를 관리하게 되어 있는데 이를 모두 bloc을 통해 제어할수 있도록 바꾸어 주어야 한다.
- bloc으로 모두 제어하기 위해서는 먼저 필요한 state, event 그리로 bloc을 정의 하여야 한다.
- state는 사용자들에게 연산의 결과를 보여줄 count만 있으면 충분하다.
- event의 경우 더하기 버튼을 눌렀을때, 빼기 버튼을 눌렀을때를 정의하는 event가 필요하게 된다.
- bloc의 경우 event를 input으로 받아 state를 아웃풋으로 전달해준다고 생각할수 있다. 그렇기 때문에 더하기 버튼, 빼기 버튼에 해당하는 bloc들이 각각 필요 하게 된다.
3. BLoC forder 보기
- bloc.dart : 이 파일은 나중 import의 편의를 위해 만들어 준 파일이다.
- counter_bloc.dart : counter_bloc.dart의 경우 사용하게될 bloc들을 모두 모아둔 파일이다.
- counter_event.dart : event들을 정의해 놓은 파일이다.
- counter_state.dart : 사용하게 될 state를 정의해 놓은 파일이다.
4. counter_state.dart
import 'package:meta/meta.dart';
@immutable
class CounterState {
final int count;//이것이 counter를 위해 사용되어질 state이다.
CounterState({@required this.count});
factory CounterState.empty() {
return CounterState(count: 0);
}//state는 초기화 과정이 필요하게 되는데 이를 위한 것이다.
CounterState update({
int count,
}) {
return copyWith(count: count);
} //bloc에서의 state업데이트를 위해 사용하게 되는 것이다.
CounterState copyWith({
int count,
}) {
return CounterState(count: count ?? this.count);
}
}
- state의 경우 항상 초기화 과정이 필요하고, bloc에서의 output은 state인데 이를 위해서 사용 되어지는 것이 update라고 보면 된다.
5. counter_event.dart
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
@immutable
abstract class CounterEvent extends Equatable {
CounterEvent([List props = const []]) : super(props);
}
class PageLoaded extends CounterEvent{
@override
String toString() {
return 'page loaded';
}
}//제일 처음 페이지가 불리게 되면 실행될 작업을 위해 선언한 이벤트
class IncrementBtnPressed extends CounterEvent{
@override
String toString() {
return "Increment button pressed";
}
}//증가 버튼을 누르게되면 발생할 event
class DecrementBtnPressed extends CounterEvent{
@override
String toString() {
return "Decrement button pressed";
}
}//감소 버튼을 누르게 되면 발생할 event
6. counter_bloc.dart
import 'package:bloc/bloc.dart';
import 'counter_event.dart';
import 'counter_state.dart';
class CounterBloc extends Bloc<CounterEvent, CounterState> {
@override
// TODO: implement initialState
CounterState get initialState => CounterState.empty();//state의 초기화를 위해 사용이 되어진다.
@override
Stream<CounterState> mapEventToState(CounterEvent event) async* {
if (event is PageLoaded) {
yield* _mapPageLodaedToState();
} else if (event is IncrementBtnPressed) {
yield* _mapIncrementBtnPressedToState();
} else if (event is DecrementBtnPressed) {
yield* _mapDecrementBtnPressedToState();
}
}//각각 이벤트에 따라 실행이 되게될 bloc들을 if문을 통해 분류하여 준다.
Stream<CounterState> _mapPageLodaedToState() async* {
yield state.update(count: 0);
}//처음 페이지가 불리게 되면 사용되어진다.
Stream<CounterState> _mapIncrementBtnPressedToState() async* {
yield state.update(count: state.count + 1);
}//증가 버튼을 부르게 되면 사용되어진다
Stream<CounterState> _mapDecrementBtnPressedToState() async* {
yield state.update(count: state.count - 1);
}//감소 버튼을 부르게 되면 사용되어진다.
}
- 각각의 블럭들은 if 문을 통해 어떤 event에 따라 불리게 될지 결정되어 지게 된다.
- bloc은 event를 input()으로 받아 적절한 조치를 취한후 state를 반환하게 된다. 이때 state 반환을 위해 yield를 사용한다.
7. bloc.dart
export 'counter_bloc.dart';
export 'counter_event.dart';
export 'counter_state.dart';
- bloc에 필요한 모든 파일을 export시켜 이파일만 import하여도 bloc관련 파일들이 모두 import 될수 있도록 한다.
8. main.dart
- 현재 main.dart의 코드는 (2.main.dart 분석 참고) add를 위한 button 밖에 없으며, BLoC가 전혀 적용이 되지 않은 상태이다.
- 먼저 감소를 위한 버튼을 먼저 만들어 주도록 한다.
//----------------------------------BEFORE-------------------------------------
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,//버튼을 누르게 되면 _incrementCounter함수가 실행이 되게 된다.
tooltip: 'Increment',
child: Icon(Icons.add),//현재는 애드를 위한 버튼 만이 존재한다.
),
//-----------------------------------AFTER-------------------------------------
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
//store btn
floatingActionButton: Container(
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(20),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {},
),
),
Spacer(
flex: 1,
),
Padding(
padding: const EdgeInsets.all(20),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {},
),
),
],
),
),
- 다음은 bloc과 연결을 해주는 작업이 필요하다 이를 위해서 제일 먼저 해야 할 작업은 flutter_bloc:^2.0.0을 pubspec.yaml 파일에 추가하고 해당 library를 import하는 작업이다.
- 그 후의 작업은 bloc을 초기화 하는 작업과 동시에 empty 를 통해 state를 초기화 하는 작업이 필요하게 된다.
- 그와 동시에 setState와 main.dart에서 선언된 _counter variable을 사용하지 않을 것이기 때문에 지우도록 하겠다.
//----------------------------------BEFORE-------------------------------------
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}//setSate를 통하여 counter가 늘어나는것을 제어 하도록 구현이 되어 있다.
@override
Widget build(BuildContext context) {
...
}
}
//-----------------------------------AFTER-------------------------------------
class _MyHomePageState extends State<MyHomePage> {
CounterBloc _counterBloc;
@override
void initState() {
// TODO: implement initState
super.initState();
_counterBloc = CounterBloc();
}
@override
Widget build(BuildContext context) {
...
}
}
- 여기서 부터는 실질적으로 BLoC을 UI쪽과 연결시키게 되는 작업을 하게 되는데 BLoC Builder를 이용한다. BLoC과 실질적으로 연결 되는 body 부분을 BlocBuilder<Bloc,state>{bloc : , builder : (context,state{})}로 감싸게 되고 해당 bloc을 계속하여 사용 하게 된다.
- 이때 Bloc 부분에는 자신이 사용할 bloc과 state부분에는 그 bloc에의해 결과가 산출되는 state를 적어 줄수 있도록 한다.
- 아래는 body 부분을 BlocBuilder로 감싸준 후의 모습을 나타낸다.
//----------------------------------BEFORE-------------------------------------
...
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
...
)
...
}
//-----------------------------------AFTER-------------------------------------
...
@override
Widget build(BuildContext context) {
return BlocBuilder(
bloc: _counterBloc,
builder: (BuildContext context, CounterState state) {
return Scaffold(
...
)
...
}
...
);
}
- 다음으로는 state값을 쓸수 있도록 하여 주어야 하는데 여기서 사용하는 state의 값은 count 값이다. 이것은 state.count를 통해 접근이 가능하다.
- counter의 값은 0으로 초기화가 되어 있는 상태이다..(counter_state.dart파일 empty에서 0으로 초기화가 된다.)
//----------------------------------BEFORE-------------------------------------
...
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:',),
Text(
'$_counter',//해당 _counter 변수는 더이상 사용을 하지 않는다.
style: Theme.of(context).textTheme.display1,),
],
),
);
...
//-----------------------------------AFTER-------------------------------------
...
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:',),
Text(
'${state.count}',//이것을 통해 사용자가 하는 행동에 맞는 state를 보여주게 된다.
style: Theme.of(context).textTheme.display1,),
],
),
);
...
- 마지막 작업으로는 각각의 버튼들에 알맞는 event를 연결하여 주게 되면 Bloc연결 작업은 끝이나게 된다.
//-----------------------------------AFTER-------------------------------------
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
//store btn
floatingActionButton: Container(
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(20),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {},
),
),
Spacer(
flex: 1,
),
Padding(
padding: const EdgeInsets.all(20),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {},
),
),
],
),
),
//-----------------------------------AFTER-------------------------------------
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
//store btn
floatingActionButton: Container(
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(20),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
_counterBloc.add(DecrementBtnPressed());
},
),
),
Spacer(
flex: 1,
),
Padding(
padding: const EdgeInsets.all(20),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
_counterBloc.add(IncrementBtnPressed());
},
),
),
],
),
),
9. main.dart 전체 코드
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import './bloc/counter_bloc/bloc.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CounterBloc _counterBloc;
@override
void initState() {
super.initState();
_counterBloc = CounterBloc();
}
@override
Widget build(BuildContext context) {
return BlocBuilder(
bloc: _counterBloc,
builder: (BuildContext context, CounterState state) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${state.count}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerFloat,
//store btn
floatingActionButton: Container(
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(20),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
_counterBloc.add(DecrementBtnPressed());
},
),
),
Spacer(
flex: 1,
),
Padding(
padding: const EdgeInsets.all(20),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
_counterBloc.add(IncrementBtnPressed());
//print(state.count);
},
),
),
],
),
),
);
});
}
}
10. 결과물
0보다 작아지는것을 막고 싶다면 counter_bloc.dart에서 _mapDecrementBtnPressedToState()부분을 건드리면 될것이다.!!
'flutter > BLoC' 카테고리의 다른 글
Flutter) Bloc으로 Todo List 만들기 (2) (5) | 2020.03.05 |
---|---|
Flutter) Bloc으로 Todo List 만들기 (1) (0) | 2020.02.28 |
Flutter) Bloc 패턴 적용을 위한 flutter_bloc widget(1) (0) | 2020.02.07 |
Flutter) BLoC 이해 (2) | 2020.01.12 |
Comments