<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>e_maker</title>
        <link>https://velog.io/</link>
        <description>즐거움을 만드는 사람</description>
        <lastBuildDate>Mon, 20 Jun 2022 00:45:37 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. e_maker. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/e_maker" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Third day to Fourth day]]></title>
            <link>https://velog.io/@e_maker/Third-day-to-Fourth-day</link>
            <guid>https://velog.io/@e_maker/Third-day-to-Fourth-day</guid>
            <pubDate>Mon, 20 Jun 2022 00:45:37 GMT</pubDate>
            <description><![CDATA[<pre><code>2일차의 내용은 5일차에도 쓰일 것이라 나중에 작성하려고 한다</code></pre><hr>
<h2 id="assetbundle">AssetBundle</h2>
<p>AssetBundle의 종류에 대해서 먼저 Check!</p>
<h3 id="defaultbundle">DefaultBundle</h3>
<blockquote>
<p>하위 항목에 대한 기본 자신 번들을 결정하는 위젯!
(명시적으로 지정하지 않은 경우 AssetImages에 사용할 변들을 결장하기 위해 Image에서 사용됌)</p>
</blockquote>
<p>example)</p>
<p>DefaultAssetBundle.of를 사용하여 AssetBundle을 가정하면
이제 TestAssetBundle의 &quot;Hello world!&quot;가 표시되옵나이다 (리소스 / 테스트를 요청할 때)</p>
<h3 id="assetsbundle-class">AssetsBundle class</h3>
<blockquote>
<p>어플리케이션에서 사용하는 리소스 모임</p>
</blockquote>
<p>assetbundle들은 어플리케이션에서 사용할 수 있는 이미지 및 문자열과 같은 리소스를 포함 =&gt; 리소스에 대한 어플리케이션의 사용자 인터페이스를 차단하지 않고 네트워크(NetworkAssetBundle) 또는 로컬 파일 시스템에서 투명하게 로드할 수 있음.</p>
<p>어플리케이션 프로그램에는 빌드될 때 응용 프로그램과 함께 패키지된 리소스가 포함된 rootBundle이 있음 / 어플리케이션의 루트번들에 리소스를 추가하려면 어플리케이션의 pubspect.yaml에서 asset 추가</p>
<h3 id="networkassetbundle">NetworkAssetBundle</h3>
<blockquote>
<p>네트워크를 통해 리소스를 로드하는 assetBundle</p>
</blockquote>
<p>asset bundle은 리소스를 캐시하진 않지만 기본네트워크 stack은 자체적으로 일정 수준의 캐시를 구현할 수 있음</p>
<pre><code class="language-dart">var baseUrl = Uri(
    scheme: &#39;https&#39;,
    host: &#39;dart.dev&#39;,
    path: &#39;/guides/libraries/library-tour&#39;,
    fragment: &#39;numbers&#39;);
NetworkAssetBundle(Uri baseUrl)

```dart
// 공식 문서의 예제

import &#39;dart:convert&#39;;
import &#39;dart:typed_data&#39;;

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter/services.dart&#39;;

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

  @override
  Widget build(BuildContext context) {
    return DefaultAssetBundle(
      bundle: TestAssetBundle(),
      child: const Text(&quot;hello World&quot;),
    );
  }
}

class TestAssetBundle extends CachingAssetBundle {
  @override
  Future&lt;ByteData&gt; load(String key) async {
    if (key == &#39;resources/test&#39;) {
      return ByteData.view(
          Uint8List.fromList(utf8.encode(&#39;Hello World!&#39;)).buffer);
    }
    return ByteData(0);
  }
}</code></pre>
<hr>
<h2 id="image--icon">image / icon</h2>
<h3 id="image">image</h3>
<p>Image 자체 위젯에는 Image , Image.network, Image.assets, Image.file, Image.memory 등이 있다.
NetworkImages, AssetsImages 등도 있다</p>
<pre><code class="language-dart">flutter:
  assets:
    - images/cat.png
  assets:
    - packages/fancy_backgrounds/backgrounds/background1.png</code></pre>
<pre><code class="language-dart">Center(
      child: Column(
        children: [
          const Image(
            image: NetworkImage(
                &#39;https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg&#39;),
          ),
          Image.network(
              &#39;https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg&#39;),
          Image.asset(&#39;icons/heart.png&#39;, package: &#39;my_icons&#39;)
        ],
      ),
    );</code></pre>
<p>그 외에 RawImage / ImageProvider 등도 있으니.. 한번 정도는 읽어볼만 한 거 같다. (잘 안쓸 거 같지만..)</p>
<h3 id="icon">icon</h3>
<p>다들 알만한 부분이라 따로 자세히 적진 않는다.</p>
<p>IconButton, Icons, IconsThenme, ImageIcon 등 여러가지로 쓰임</p>
<pre><code class="language-dart">  Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: const &lt;Widget&gt;[
              Icon(
                Icons.favorite,
                color: Colors.pink,
                size: 24.0,
                semanticLabel: &#39;Text to announce in accessibility modes&#39;,
              ),
              Icon(
                Icons.audiotrack,
                color: Colors.green,
                size: 30.0,
              ),
              Icon(
                Icons.beach_access,
                color: Colors.blue,
                size: 36.0,
              ),
            ],
          ),

   // const IconTheme(
//{Key? key,
//required IconThemeData data,
//required Widget child}
//) 
// 위와 같은 방식으로 쓰인다
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[First Day]]></title>
            <link>https://velog.io/@e_maker/First-Day</link>
            <guid>https://velog.io/@e_maker/First-Day</guid>
            <pubDate>Thu, 16 Jun 2022 14:24:20 GMT</pubDate>
            <description><![CDATA[<h2 id="one-day-widget">One Day Widget</h2>
<blockquote>
<p>하루에 Flutter Widget을 하나 이상을 예제를 보면서 이해하는 시간을 가지기 위해 준비한 시간</p>
</blockquote>
<h3 id="today-widget-semantics">Today Widget (Semantics)</h3>
<p>  // 위젯의 의미에 대한 설명으로 위젯 트리에 주석을 추가하는 위젯!!
 // 접근성 도구 , 검색 엔진 및 기타 의미 분석 소프트웨어에서 용응 프로그램의 의미를 결정하는 데 사용!!</p>
<ul>
<li>예제 </li>
</ul>
<pre><code class="language-dart">import &#39;package:flutter/material.dart&#39;;

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

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 10.0),
      width: double.infinity,
      height: 200,
      child: Semantics(
        label: &quot;Free Image&quot;,
        child: const CircleAvatar(
          radius: 25.0,
          backgroundImage: NetworkImage(&quot;https://post-phinf.pstatic.net/MjAyMTAxMjVfMTA1/MDAxNjExNTU3Mzk4MjIy.MEUMpAkus932Fhh-pbLvJrTfgcRSa9lLqnSzItTVjOcg.ua3bCg2MM04cjQR1LUjdol1vgYEhnSmUF-BMDWyZW7Ig.PNG/%EA%B7%80%EC%97%AC%EC%9A%B4%EC%9D%BC%EB%9F%AC%EC%8A%A4%ED%8A%B8.png?type=w1200&quot;),
          child: Text(
            &quot;우리 고양이&quot;,
            style: TextStyle(color: Colors.red),
          ),
        ),
      ),
    );
  }
}
</code></pre>
<hr>
<h3 id="today-widget-merge-semantics">Today Widget (merge Semantics)</h3>
<ul>
<li>하위 항목의 semantics를 병합하는 위젯</li>
<li>이 노드에 뿌리를 둔 하위 트리의 모든 semantics 체계가 semantic 체계 트리의 한 노드를 병합되도록 함</li>
</ul>
<ul>
<li><p>체크박스 위젯 옆에 text node가 있는 위젯의 경우 checkbox의 checked semantic status를 가진 Text 노드의 label을 레이블이 모두 있는 단일 노드로 사용할 수 있음</p>
</li>
<li><p>하위 트리의 두 노드에 semantics가 충돌하는 경우 결과가 무의미 할 수 있음</p>
</li>
<li><p>체크박스가 체크되어 있고 체크박스가 해제되어 있는 서브트리는 체크된 것으로 표시</p>
</li>
<li><p>label은 단일 문자열로 merge / 각 레이블을 다른 레이블과 구분하는 개행 / merge된 하위 트리의 여러 노드</p>
</li>
<li><p>semantics 제스쳐를 처리할 수 있는 경우 tree 순서의 첫번 쨰 노드가 콜백을 수신하는 노드가 됌</p>
</li>
</ul>
<pre><code class="language-dart">import &#39;package:flutter/material.dart&#39;;

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

  @override
  Widget build(BuildContext context) {
    return Container(
        padding: const EdgeInsets.symmetric(vertical: 10.0),
        width: double.infinity,
        height: 200,
        child: MergeSemantics(
          // 하위 항목의 semantics를 병합하는 위젯
          // 이 노드에 뿌리를 둔 하위 트리의 모든 semantics 체계가 semantic 체계 트리의 한 노드를 병합되도록 함
          // 체크박스 위젯 옆에 text node가 있는 위젯의 경우 checkbox의 checked semantic status를 가진 Text 노드의 label
          // 을 레이블과 레이블이 모두 있는 단일 노드로 사용할 수 있음

          // 하위 트리의 두 노드에 semantics가 충돌하는 경우 결과가 무의미 할 수 있음
          // 체크박스가 체크되어 있고 체크박스가 해제되어 있는 서브트리는 체크된 것으로 표시
          // label은 단일 문자열로 merge / 각 레이블을 다른 레이블과 구분하는 개행 / merge된 하위 트리의 여러 노드
          // semantics 제스쳐를 처리할 수 있는 경우 tree 순서의 첫번 쨰 노드가 콜백을 수신하는 노드가 됌
          child: Row(
            children: &lt;Widget&gt;[
              Checkbox(
                value: true,
                onChanged: (bool? value) {},
              ),
              const Text(&#39;Settings&#39;),
            ],
          ),
        ));
  }
}

</code></pre>
<hr>
<h3 id="today-widget-exclude_semantics">Today Widget (exclude_semantics)</h3>
<ul>
<li>child 항목의 모든 자손의 시맨틱스를 삭제하는 위젯!</li>
<li>excluding이 true라면, 해당 위젯(해당 child tree)에서 제외 되옴</li>
<li>혼란스러운 하위 위젯을 숨기는 데 사용하 수 있음</li>
<li>example (material library chip 위젯은 chip label과 중복되기에 아바타를 숨킬 수 있음)</li>
</ul>
<pre><code class="language-dart">import &#39;package:flutter/material.dart&#39;;

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

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 10.0),
      width: double.infinity,
      height: 200,
      child: const ExcludeSemantics(

        excluding: true,
        child: CircleAvatar(
          radius: 25.0,
          backgroundImage: NetworkImage(&quot;https://post-phinf.pstatic.net/MjAyMTAxMjVfMTA1/MDAxNjExNTU3Mzk4MjIy.MEUMpAkus932Fhh-pbLvJrTfgcRSa9lLqnSzItTVjOcg.ua3bCg2MM04cjQR1LUjdol1vgYEhnSmUF-BMDWyZW7Ig.PNG/%EA%B7%80%EC%97%AC%EC%9A%B4%EC%9D%BC%EB%9F%AC%EC%8A%A4%ED%8A%B8.png?type=w1200&quot;),
          child: Text(
            &quot;우리 고양이2&quot;,
            style: TextStyle(color: Colors.blue),
          ),
        ),
      ),
    );
  }
}
</code></pre>
<hr>
<h3 id="today-widget-block-semantics">Today Widget (Block Semantics)</h3>
<ul>
<li>BlockSemantics</li>
<li>동일한 semantics 컨테이너에서 이전에 그려진 모든 위젯의 semantics를 삭제하는 위젯</li>
<li>특정 위젯 뒤에 그려진 접근성 도구에서 위젯을 숨기는 데 유용</li>
<li>ex) Drawer은 drawer 외부 모든 위젯과 상호작용을 차단함</li>
</ul>
<pre><code class="language-dart">
import &#39;package:flutter/material.dart&#39;;

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

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 10.0),
      width: double.infinity,
      height: 200,
      child: BlockSemantics(
        blocking: true,
        child: const CircleAvatar(
          radius: 25.0,
          backgroundImage: NetworkImage(&quot;https://post-phinf.pstatic.net/MjAyMTAxMjVfMTA1/MDAxNjExNTU3Mzk4MjIy.MEUMpAkus932Fhh-pbLvJrTfgcRSa9lLqnSzItTVjOcg.ua3bCg2MM04cjQR1LUjdol1vgYEhnSmUF-BMDWyZW7Ig.PNG/%EA%B7%80%EC%97%AC%EC%9A%B4%EC%9D%BC%EB%9F%AC%EC%8A%A4%ED%8A%B8.png?type=w1200&quot;),
          child: Text(
            &quot;우리 고양이3&quot;,
            style: TextStyle(color: Colors.red),
          ),
        ),
      ),
    );
  }
}

</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[bloc extension_methods]]></title>
            <link>https://velog.io/@e_maker/bloc-extensionmethods</link>
            <guid>https://velog.io/@e_maker/bloc-extensionmethods</guid>
            <pubDate>Tue, 07 Jun 2022 08:52:56 GMT</pubDate>
            <description><![CDATA[<h2 id="bloc">bloc</h2>
<p>다트 27에서 확장된 방법이 하나 추가되었다. </p>
<p>flutter_bloc이 provider 패키지에 종속되어 상속된 위젯의 사용이 단순화됌</p>
<p>blocProvider, MultiBlocProvider, RepositoryProvider, MultiRepositoryProvider widgets, flutter_bloc exports the ReadContext, WatchContext, selectContext 확장자를 export함</p>
<h4 id="contextreadt">context.read<T>()</h4>
<p>가장 가까운 상위 인스턴스를 검색하며, 기능적으로는 BlocProvider.of(context)와 동일, context.read는 onPressed callback에서 event add 하기 위해 bloc instance 검색하는데 사용! </p>
<p>context.read()는 T를 Listener하지 못함 .. 제공된 개체 유형 T가 변경되면 context.read는 위젯 재구성을 trigger하지 않음</p>
<pre><code class="language-dart">
// Do
onPressed() {
  context.read&lt;CounterBloc&gt;().add(CounterIncrementPressed()),
}

// Avoid - build method 안에서는 context.read 사용을 피해라
@override
Widget build(BuildContext context) {
  final state = context.read&lt;MyBloc&gt;().state;
  return Text(&#39;$state&#39;);
}

// bloc state change =&gt; text widget 다시 작성 x =&gt; 오류 발생하기 쉬움 =&gt; blocBuilder 또는 context.watch를 사용함 / 상태 변화에 대응하여 다시 빌드하기 위해!! 
</code></pre>
<hr>
<h4 id="contextwatcht">context.watch<T>()</h4>
<p>T 유형의 가장 가까운 상위 인스턴스를 제공하지만 인스턴스의 변경 내용도 Listening 함 / 기능적으로 blocProvider.of(context, listen:true)와 동일함</p>
<p>제공된 유형 T의 개체가 변경되는 경우 context.watch가 트리거를 재빌드함
context.watch는 단지 엑세스 할 수 있는데, statelesswidget이나 상태클래스의 빌드 메소드 안에서만!!.</p>
<pre><code class="language-dart">// Do - use BlocBuilder instead of context.watch to explicitly scope rebuilds.
Widget build(BuildContext context) {
  return MaterialApp(
    home: Scaffold(
      body: BlocBuilder&lt;MyBloc, MyState&gt;(
        builder: (context, state) {
          // Whenever the state changes, only the Text is rebuilt.
          return Text(state.value);
        },
      ),
    ),
  );
}
// alternatively 
@override
Widget build(BuildContext context) {
  return MaterialApp(
    home: Scaffold(
      body: Builder(
        builder: (context) {
          // Whenever the state changes, only the Text is rebuilt.
          final state = context.watch&lt;MyBloc&gt;().state;
          return Text(state.value);
        },
      ),
    ),
  );
}
// Do - use Builder and context.watch as MultiBlocBuilder.
Builder(
  builder: (context) {
    final stateA = context.watch&lt;BlocA&gt;().state;
    final stateB = context.watch&lt;BlocB&gt;().state;
    final stateC = context.watch&lt;BlocC&gt;().state;

    // return a Widget which depends on the state of BlocA, BlocB, and BlocC
  }
);

//Avoid - 빌드 메소드의 상위 위젯이 상태에 따라 달라지지않는 지 확인

@override
Widget build(BuildContext context) {
  // Whenever the state changes, the MaterialApp is rebuilt
  // even though it is only used in the Text widget.
  final state = context.watch&lt;MyBloc&gt;().state;
  return MaterialApp(
    home: Scaffold(
      body: Text(state.value),
    ),
  );
}

</code></pre>
<hr>
<h4 id="contextselect">context.select</h4>
<blockquote>
<p>context.watch&lt;T.(), context.select&lt;T,R&gt;(R function(T Value)) provide =&gt; t 유형의 가장 가까운 상위 인스턴스를 제공하고, 변경 내용을 listening 함!! context.watch와 다르게, context.select를 사용하면 상태의 더 작은 부분에서 변경 사항을 listening 할 수 있음</p>
</blockquote>
<pre><code class="language-dart">
Widget build(BuildContext context) {
  final name = context.select((ProfileBloc bloc) =&gt; bloc.state.name);
  return Text(name);
}
// profileBloc의 속성 이름이 변경될 때만 위젯을 재구성함

//Do - context 대신 Bloc Selector를 사용하기 =&gt; 재 빌드 범위를 명시적으로 지정하려면 선택
Widget build(BuildContext context) {
  return MaterialApp(
    home: Scaffold(
      body: BlocSelector&lt;ProfileBloc, ProfileState, String&gt;(
        selector: (state) =&gt; state.name,
        builder: (context, name) {
          // Whenever the state.name changes, only the Text is rebuilt.
          return Text(name);
        },
      ),
    ),
  );
}

//alternatively
@override
Widget build(BuildContext context) {
  return MaterialApp(
    home: Scaffold(
      body: Builder(
        builder: (context) {
          // Whenever state.name changes, only the Text is rebuilt.
          final name = context.select((ProfileBloc bloc) =&gt; bloc.state.name);
          return Text(name);
        },
      ),
    ),
  );
}

//Avoid - context.select를 사용할 때 빌드 메소드의 상위 위젯은 상태에 따라 달라지지 않음
@override
Widget build(BuildContext context) {
  // Whenever the state.value changes, the MaterialApp is rebuilt
  // even though it is only used in the Text widget.
  final name = context.select((ProfileBloc bloc) =&gt; bloc.state.name);
  return MaterialApp(
    home: Scaffold(
      body: Text(name),
    ),
  );
}

// context.select를 사용하여, 선택 항목이 변경될 떄 전체 위젯이 다시 재빌드 돼요.
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[flutter / bloc]]></title>
            <link>https://velog.io/@e_maker/flutter-bloc</link>
            <guid>https://velog.io/@e_maker/flutter-bloc</guid>
            <pubDate>Tue, 07 Jun 2022 07:58:15 GMT</pubDate>
            <description><![CDATA[<p><strong>dart에서 쓰이는 bloc package가 아닌 flutter에서 쓰이는 flutter_bloc 패키지에 관한 것임</strong></p>
<h2 id="flutter_bloc">flutter_bloc</h2>
<h3 id="bloc-widget">Bloc Widget</h3>
<hr>
<blockquote>
<h4 id="blocbuilder">BlocBuilder</h4>
<p>bloc과 builder 기능을 사용하기 위한 flutter widget . block builder는 새 상태에 대한 응답으로 위젯 작성 처리, blocBuilder는 streamBuilder와 매우 유사하지만 필요한 보일러 플레이트 코드의 양을 줄기이 위한 더 간단한 api를 가지고 있음! / builder 함수는 잠재적으로 여러번 호출 될 수 있음 / state response =&gt; 위젯 return pure function</p>
</blockquote>
<p>대화 상자 표시 등과 같은 상태 변경에 대해 수행하려면 blocListener를 참조!</p>
<p>블록 매개 변수를 생략하면, blocBuilder가 blocProvider 및 현재 buildcontext를 사용하여 자동으로 조회를 수행함</p>
<pre><code class="language-dart">
BlocBuilder&lt;BlocA, BlocAState&gt;(
    // blocProvider, BlocState
  builder: (context, state) {
    // return widget here based on BlocA&#39;s state
  }
)

// 단일 위젯으로 범위가 지정, 상위 blocProvider 및 
//현재 BuildContext를 통해 access 할 수 없는 bloc 제공하려는 경우에만 bloc 지정

BlocBuilder&lt;BlocA, BlocAState&gt;(
  bloc: blocA, // provide the local bloc instance
  builder: (context, state) {
    // return widget here based on BlocA&#39;s state
  }
)
// true를 return 하면, 빌더와 상태가 함꼐 호출 =&gt; 위잿 재구성
// false일 경우 return 하면, 상태와 함꼐 호출되지 않아 위젯 재구성하지 않음
</code></pre>
<hr>
<h4 id="blocselector">BlocSelector</h4>
<blockquote>
<p>blocSelector는 blocBuilder와 유사히자만 블록 상태를 기준으로 새 값을 선택하여 업데이트를 필터링할 수 있는 위젯! / 값이 변경되지 않으면 불필요한 빌드 방지 / blocSelector가 builder를 다시 호출 여부는 결정하려면 값이 불변해야함!! =&gt; blocSelector 가 blockProvider 및 buildContext를 사용하여 조회를 자동으로 수행</p>
</blockquote>
<pre><code class="language-dart">BlocSelector&lt;BlocA, BlocAState, SelectedState&gt;(
  selector: (state) {
    // return selected state based on the provided state.
  },
  builder: (context, state) {
    // return widget here based on the selected state.
  },
)</code></pre>
<hr>
<h4 id="blocprovider">BlocProvider</h4>
<blockquote>
<p>BlockProvider.of<T>(context)를 통해 자식들에게 bloc을 제공하는 Flutter 위젯!
하위 tree 내의 여러 위젯에 bloc의 단일 instance를 제공할 수 있도록 종속성 주입 위젯(해당 객체를 클래스마다 인스턴스화 즉 생성하는 대신 클래스에 종속 객체를 주입)으로 사용
bloc provider를 사용하여 하위 트리 나머지 부분에 사용할 수 있는 새 블록을 만들어야 함! / BlockProvider는 블록을 만드는 역할을 하므로 block close를 자동으로 함!</p>
</blockquote>
<p>기본값으로, blocProvider는 BlocProvidr.of<BlocA>(context)를 통해 bloc을 조회할 떄 Create 실행
위의 동작을 무시하고 즉시 실행하도록 하려면 lazy를 false로 설정</p>
<pre><code class="language-dart">BlocProvider(
  lazy: false,
  create: (BuildContext context) =&gt; BlocA(),
  child: ChildA(),
);
//BlocProvider를 사용하여, 위젯 트리의 새 부분에 기존 bloc을 provide 할 수 있음 
// 기존 bloc을 새 path에 사용할 수 있어야 할 떄 가장 일반적으로 사용 
// blockProvider는 bloc create X =&gt; bloc을 자동으로 close하지 않음!
BlocProvider.value(
  value: BlocProvider.of&lt;BlocA&gt;(context),
  child: ScreenA(),
);

// with extensions
context.read&lt;BlocA&gt;();

// without extensions
BlocProvider.of&lt;BlocA&gt;(context)</code></pre>
<hr>
<h4 id="multiblocprovider">MultiBlocProvider</h4>
<blockquote>
<p>MultiBlocProvider는 여러 blocProvider를 하나로 병합하는 widget
MultiBlocProvider는 가독성 향상 / 여러 BlocProvider를 중첩할 필요 x </p>
</blockquote>
<pre><code class="language-dart">
BlocProvider&lt;BlocA&gt;(
  create: (BuildContext context) =&gt; BlocA(),
  child: BlocProvider&lt;BlocB&gt;(
    create: (BuildContext context) =&gt; BlocB(),
    child: BlocProvider&lt;BlocC&gt;(
      create: (BuildContext context) =&gt; BlocC(),
      child: ChildA(),
    )
  )
)

// to

MultiBlocProvider(
  providers: [
    BlocProvider&lt;BlocA&gt;(
      create: (BuildContext context) =&gt; BlocA(),
    ),
    BlocProvider&lt;BlocB&gt;(
      create: (BuildContext context) =&gt; BlocB(),
    ),
    BlocProvider&lt;BlocC&gt;(
      create: (BuildContext context) =&gt; BlocC(),
    ),
  ],
  child: ChildA(),
)
</code></pre>
<hr>
<h4 id="bloclistener">BlocListener</h4>
<blockquote>
<p>BlocListener는 BlocWidgetListener와 선택적 Bloc을 사용하고 Bloc의 상태 변화에 따라 Listener를 호출하는 위젯 (snackBar, navigation, dialog에 사용 =&gt; 상태 변경당 한번 발생하는 기능!!)
Listener는 BlocBuilder의 builder와 달리 각 상태 변경(initial state는 포함되지 않음)에 대해 한 번만 호출되며 무효함수임! / Bloc 매겨 변수 생략 시 BlocListener가 BlocProvider 및 현재 BuildContext를 사용하여 조회를 자동으로 수행</p>
</blockquote>
<pre><code class="language-dart">BlocListener&lt;BlocA, BlocAState&gt;(
  listener: (context, state) {
    // do stuff here based on BlocA&#39;s state
  },
  child: Container(),
)

//BlocProvider 및 현재 BuildContext를 통해 Access 할 수 없는 bloc을 제공하려는 경우에만 bloc 지정
BlocListener&lt;BlocA, BlocAState&gt;(
  bloc: blocA,
  listener: (context, state) {
    // do stuff here based on BlocA&#39;s state
  },
  child: Container()
)

// function을 선택적으로 호출하려면, listenWhen을 사용하면 된다. 
//listen 이전 bloc state와 현재 bloc state를 사용하고 있는 bool을 return 해줌 =&gt; 
//true라면 listener와 상태가 같이 호출 / false라면 함꼐 호출되지 않음

BlocListener&lt;BlocA, BlocAState&gt;(
  listenWhen: (previousState, state) {
    // return true/false to determine whether or not
    // to call listener with state
  },
  listener: (context, state) {
    // do stuff here based on BlocA&#39;s state
  },
  child: Container(),
)</code></pre>
<hr>
<h4 id="multibloclistener">MultiBlocListener</h4>
<blockquote>
<p>여러 BlocListener 위젯을 하나로 병합하는 위젯 / 가독성 향상 , 중첩할 필요 없음</p>
</blockquote>
<pre><code class="language-dart">
BlocListener&lt;BlocA, BlocAState&gt;(
  listener: (context, state) {},
  child: BlocListener&lt;BlocB, BlocBState&gt;(
    listener: (context, state) {},
    child: BlocListener&lt;BlocC, BlocCState&gt;(
      listener: (context, state) {},
      child: ChildA(),
    ),
  ),
)

//to

MultiBlocListener(
  listeners: [
    BlocListener&lt;BlocA, BlocAState&gt;(
      listener: (context, state) {},
    ),
    BlocListener&lt;BlocB, BlocBState&gt;(
      listener: (context, state) {},
    ),
    BlocListener&lt;BlocC, BlocCState&gt;(
      listener: (context, state) {},
    ),
  ],
  child: ChildA(),
)

</code></pre>
<hr>
<h4 id="blocconsumer">BlocConsumer</h4>
<blockquote>
<p>BlocConsumer 는 새로운 상태에 반응하기 위해 builder와 listener를 노출시킴 / 중첩된 blocListener 및 BlocBuilder와 유사하지만 코드의 양을 줄임
BlocConsumer는 UI를 재구성하고 bloc의 상태 변화에 대한 다른 대응을 실행하는 경우에만 사용해야함
BlocConsumer는 필수 BlocWidgetBuilde 및 BlocWidgetListener와 선택적 bloc, BlockBuilderCondition 및 BlocListenerCondition을 사용
bloc 매개 변수 생략시 BlocConsumer는 BlocProvider 및 현재 BuildContext를 사용하여 조회를 자동으로 수행</p>
</blockquote>
<pre><code class="language-dart">BlocConsumer&lt;BlocA, BlocAState&gt;(
  listener: (context, state) {
    // do stuff here based on BlocA&#39;s state
  },
  builder: (context, state) {
    // return widget here based on BlocA&#39;s state
  }
)

//선택적으로 ListenWhen과 buildWhen을 사용할 수 있음 
//listenWhen 과 buildWhen은 각각의 bloc state 변경에 대해 호출됌
// 각각 이전 상태와 현재 상태를 취하며, 빌더 및 listener 기능의 호출 여부를 결정하는 모음을 반환 
// Bloc Consumer가 초기화되면 이전 상태의 Bloc Consumer가 초기화 됌 / listenWhen과 buildWhen이 구현되지 않는 경우 기본적으로 true로 설정됌

BlocConsumer&lt;BlocA, BlocAState&gt;(
  listenWhen: (previous, current) {
    // return true/false to determine whether or not
    // to invoke listener with state
  },
  listener: (context, state) {
    // do stuff here based on BlocA&#39;s state
  },
  buildWhen: (previous, current) {
    // return true/false to determine whether or not
    // to rebuild the widget with state
  },
  builder: (context, state) {
    // return widget here based on BlocA&#39;s state
  }
)</code></pre>
<hr>
<h4 id="repositoryprovider">RepositoryProvider</h4>
<blockquote>
<p>RepositoryProvider는 RepositoryProvider.of<T>(context)을 통해 Child에게 repository를 제공하는 위젯
하위 트리 내의 여러 위젯에 repository의 single instance를 제공할 수 있도록 DI Widget으로 사용 / bloc을 제공하려면 BlocProvider를 사용해야 하지만, RepositoryProvider는 Repository에 대해서만 사용!</p>
</blockquote>
<pre><code class="language-dart">
RepositoryProvider(
  create: (context) =&gt; RepositoryA(),
  child: ChildA(),
);

// childA에서 다음을 사용하여 repository instance 검색할 수 있음

// with extensions
context.read&lt;RepositoryA&gt;();

// without extensions
RepositoryProvider.of&lt;RepositoryA&gt;(context)</code></pre>
<hr>
<h4 id="multirepositoryprovider">MultiRepositoryProvider</h4>
<blockquote>
<p>MultiRepositoryProvider는 여러 Repository 위젯을 하나로 병합하는 위젯!</p>
</blockquote>
<pre><code class="language-dart">
RepositoryProvider&lt;RepositoryA&gt;(
  create: (context) =&gt; RepositoryA(),
  child: RepositoryProvider&lt;RepositoryB&gt;(
    create: (context) =&gt; RepositoryB(),
    child: RepositoryProvider&lt;RepositoryC&gt;(
      create: (context) =&gt; RepositoryC(),
      child: ChildA(),
    )
  )
)

// to

MultiRepositoryProvider(
  providers: [
    RepositoryProvider&lt;RepositoryA&gt;(
      create: (context) =&gt; RepositoryA(),
    ),
    RepositoryProvider&lt;RepositoryB&gt;(
      create: (context) =&gt; RepositoryB(),
    ),
    RepositoryProvider&lt;RepositoryC&gt;(
      create: (context) =&gt; RepositoryC(),
    ),
  ],
  child: ChildA(),
)
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[dart / bloc 3 (cubit vs blog)]]></title>
            <link>https://velog.io/@e_maker/dart-bloc-3-cubit-vs-blog</link>
            <guid>https://velog.io/@e_maker/dart-bloc-3-cubit-vs-blog</guid>
            <pubDate>Tue, 07 Jun 2022 07:57:08 GMT</pubDate>
            <description><![CDATA[<h2 id="bloc">bloc</h2>
<h4 id="cubit-vs-bloc">Cubit vs Bloc</h4>
<p>큐빗을 사용해야할 떄와 블록을 사용해야할 때 비교해보기</p>
<hr>
<blockquote>
<p>cubit을 사용하는 가장 큰 장점은 simple! / 큐빗을 만들 때 상태를 변경하기 위해 노출하려는 함수와 상태를 정의하기만 하면 되옵니다. 블록 생성시 상태, 이벤트 및 이벤트핸들러 구현 정의
큐빗을 더 쉽게 이해할 수 있고 관련된 코드가 더 적음</p>
</blockquote>
<p>CounterCubit vs CounterBloc</p>
<pre><code class="language-dart">class CounterCubit extends Cubit&lt;int&gt; {
  CounterCubit() : super(0);

  void increment() =&gt; emit(state + 1);
}

abstract class CounterEvent {}
class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc&lt;CounterEvent, int&gt; {
  CounterBloc() : super(0) {
    on&lt;CounterIncrementPressed&gt;((event, emit) =&gt; emit(state + 1));
  }
</code></pre>
<p>큐빗 구현은 더 간결하고 이벤트를 별도로 정의하는 대신, 함수가 이벤트처럼 작동! 큐빗을 사용할 때 상태 변화를 트리거하기 위해 어디에서나 emit 호출 가능!</p>
<hr>
<p>큐빗과 다르게 bloc은 추적성이 좋음</p>
<p>상태 변화와 순서와 이러한 변화를 촉발한 원인을 정확히 알고 있다는 것임! / 어플리케이션의 기능에 중요한 상태의 경우, 상태 변경뿐만 아니라 모든 이벤트를 포착하기 위해, 이벤트 중심적인 접근 방식을 사용하는 것이 매우 유용</p>
<pre><code class="language-dart">
enum AuthenticationState { unknown, authenticated, unauthenticated }

</code></pre>
<p>인증됨에서 인증되지 않음으로 변경될 때 / 사용자가 로그아웃 버튼을 눌러 응용 프로그램에서 로그아웃 하도록 요청 했을 수도 있음 / 반면 사용자의 액세스 토큰이 해지되어 강제 로그아웃이 가능 =&gt; bloc 사용 시 어플리케이션 상태가 어떻게 특정 상태가 되었는지 추적 가능!</p>
<pre><code class="language-dart">
Transition {
  currentState: AuthenticationState.authenticated,
  event: LogoutRequested,
  nextState: AuthenticationState.unauthenticated
}

Change {
  currentState: AuthenticationState.authenticated,
  nextState: AuthenticationState.unauthenticated
}
// 위의 전환은 상태가 왜 변했는 지 이해하는데 필요한 모든 정보 제공 / 큐빗을 사용하면 어떻게 변하는지 디버깅하고 이해하는 데 중요한 이유 설명되지 않음!
</code></pre>
<p>진보된 이벤트 변환
bloc이 cubit보다 좋을 떄는 buffer, debounceTime, throttle 등과 같은 반응형 연산자를 활용해야할 때 유용함</p>
<p>블록에는 들어온느 이벤트의 흐름을 제어 / 변환할 수 있는 이벤트 싱크 있음
블록을 사용하면 들어오는 이벤트가 블록에 의해 처리되는 방식을 변경할 수 있는 사용자 정의 이벤트 변환기를 제공</p>
<pre><code class="language-dart">
EventTransformer&lt;T&gt; debounce&lt;T&gt;(Duration duration) {
  return (events, mapper) =&gt; events.debounceTime(duration).flatMap(mapper);
}

CounterBloc() : super(0) {
  on&lt;Increment&gt;(
    (event, emit) =&gt; emit(state + 1),
    /// Apply the custom `EventTransformer` to the `EventHandler`.
    transformer: debounce(const Duration(milliseconds: 300)),
  );
}

</code></pre>
<p>어떤 것을 사용해야 할지 여전히 잘 모르겠으면 큐빗부터 시작하여 나중에 필요에 따라 블록으로 리팩터링하거나 스케일업 할 수 있음!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[dart / bloc 2 (bloc)]]></title>
            <link>https://velog.io/@e_maker/dart-bloc-2-bloc</link>
            <guid>https://velog.io/@e_maker/dart-bloc-2-bloc</guid>
            <pubDate>Tue, 07 Jun 2022 07:55:51 GMT</pubDate>
            <description><![CDATA[<h2 id="bloc">Bloc</h2>
<p>bloc은 이벤트에 의존하여 상태 변경을 하는 진보된 클래스! 블록은 blocbase를 extends하며, 큐빗과 비슷한 api를 가지고 있음! / bloc은 이벤트를 수신하고 들어오는 이벤트를 나가는 상태로 변환!! </p>
<p><img src="https://i.imgur.com/etF7xWd.png" alt="bloc"></p>
<hr>
<h4 id="bloc-생성">bloc 생성</h4>
<p>bloc을 만드는 것은 우리가 관리할 상태를 정의하는 것 외에 bloc이 처리할 수 있는 이벤트도 정의해야 함 (이부분 외에는 cubit과 비슷)</p>
<p>event는 블록에 대한 input / 일반적으로 버튼 누르기와 같은 사용자 상호 작용이나 page load와 같은 수명 주기 이벤트에 응답하여 추가!!</p>
<pre><code class="language-dart">
abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc&lt;CounterEvent, int&gt; {
  CounterBloc() : super(0);
}

// 위에서 counter cubit을 만들 떄처럼 super를 통해 superclass에 전달하여 초기 상태를 지정!
</code></pre>
<h4 id="state-changes">state Changes</h4>
<p>블록은 큐빗의 함수가 아닌 on<Event>를 통해 이벤트 핸들러를 등록하도록 요구 =&gt; 이벤트 핸들러는 수신 이벤트를 0개 이상의 받는 상태로 변환하는 역할을 함</p>
<pre><code class="language-dart">abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc&lt;CounterEvent, int&gt; {
  CounterBloc() : super(0) {
    on&lt;CounterIncrementPressed&gt;((event, emit) {
      // handle incoming `CounterIncrementPressed` event
    })
  }
}
// 이벤트 핸들러는 추가된 이벤트와 수신 이벤트에 응답하여 0개 이상의 상태를 방출하는 데,
// 사용할 수 있는 emitter에 엑세스 할 수 있음!!
// 이벤트 핸들러를 업데이트 할 수 있다면, counterIncrementPressed 이벤트를 다룰 수 있음!!

abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc&lt;CounterEvent, int&gt; {
  CounterBloc() : super(0) {
    on&lt;CounterIncrementPressed&gt;((event, emit) {
      emit(state + 1);
    });
  }
}

// 모든 카운터를 관라히기 위해 이벤트 핸들러 등록 =&gt; 들어오는 각 카운터에 대하여 incrementPressed 이벤트는 
//상태 gettter 및 emit(state + 1)을 통해 블록의 현재 상태에 엑세스 할 수 있음!!

// bloc class는 blocbase를 확장하므로 cubit과 동일하게 state getter에서 언제든지 블록의 현재 상태에 엑세스 가능

// bloc은 절대 새로운 상태를 직접적으로 emit하면 안됌! 
// 대신 이벤트 핸들러 내의 수신 이벤트에 대한 응답으로 모든 상태 변경 output해야함!

// 블록과 큐빗 모두 중복 상태 무시 / state와 nextstate를 emit하면 상태 변경이 발생하지 않음
</code></pre>
<h4 id="bloc-사용-counter-block-인스턴스-생성하여-사용-가능--예시임">bloc 사용 (counter block 인스턴스 생성하여 사용 가능 =&gt; 예시임)</h4>
<pre><code class="language-dart">
Future&lt;void&gt; main() async {
  final bloc = CounterBloc();
  print(bloc.state); // 0
  bloc.add(CounterIncrementPressed());
  await Future.delayed(Duration.zero);
  print(bloc.state); // 1
  await bloc.close();
}

// counterbloc 인스턴스 생성 =&gt; 현재 상태 출력 (아직 새로운 상태 emit이 되지 않음) =&gt; 
//counter 추가 =&gt; incrementPressed 이벤트를 발생시켜, 상태 변경 트리거 =&gt;
// 0 -&gt; 1로 바뀐 bloc의 상태를 다시 print =&gt; bloc close =&gt; 내부 스트림 닫음

// 지연을 추가하여 이벤트 루프 반복을 기다림!!
</code></pre>
<h4 id="stream-usage">stream Usage</h4>
<p>큐빗과 마찬가지로 블록은 stream의 특수한 유형 / bloc에 구독하여 실시간으로 상태를 업데이트 할 수 있음</p>
<pre><code class="language-dart">
Future&lt;void&gt; main() async {
  final bloc = CounterBloc();
  final subscription = bloc.stream.listen(print); // 1
  bloc.add(CounterIncrementPressed());
  await Future.delayed(Duration.zero);
  // 구독 취소가 바로 되지 않도록 delayed를 줌
  await subscription.cancel();
  await bloc.close();
}

// counterbloc subscription =&gt; 상태 변경에 대한 인쇄 호출 =&gt; counter 추가 =&gt; 
//incrementPressed event 발생 =&gt; on&lt;Counter&gt; trigger =&gt; 
//이벤트 처리 및 new state emit =&gt; update no want subscription.cancel * bloc close 

</code></pre>
<p>bloc Observing (bloc extends blocCase하므로, change 사용하여 bloc의 모든 상태 변화를 observing 가능!)</p>
<pre><code class="language-dart">
abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc&lt;CounterEvent, int&gt; {
  CounterBloc() : super(0) {
    on&lt;CounterIncrementPressed&gt;((event, emit) =&gt; emit(state + 1));
  }

  @override
  void onChange(Change&lt;int&gt; change) {
    super.onChange(change);
    print(change);
  }
}


void main() {
  CounterBloc()
    ..add(CounterIncrementPressed())
    ..close();
}
// main에서 update함

Change { currentState: 0, nextState: 1 }
// 블록과 cubit의 한 가지 주요 차별화 요소는 bloc이 이벤트 중심이기에, 
// 상태변화를 유발한 원인에 대한 정보도 포착할 수 있음 (onTransition을 overriding 하여, 작업 수행)

// 한 상태에서 다른 상태로의 변화를 전환 =&gt; 전환은 현재 상태, 이벤트 및 다음 상태로 구성!!

abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc&lt;CounterEvent, int&gt; {
  CounterBloc() : super(0) {
    on&lt;CounterIncrementPressed&gt;((event, emit) =&gt; emit(state + 1));
  }

  @override
  void onChange(Change&lt;int&gt; change) {
    super.onChange(change);
    print(change);
  }

  @override
  void onTransition(Transition&lt;CounterEvent, int&gt; transition) {
    super.onTransition(transition);
    print(transition);
  }
}

Transition { currentState: 0, event: Increment, nextState: 1 }
// onTransition은 onChange 이전에 호출 / 현재 상태에서 다음 상태로 변경을 트리거한 이벤트 포함

Change { currentState: 0, nextState: 1 }
</code></pre>
<h4 id="blocobserver">BlocObserver</h4>
<p>사용자 정의 blocObserver에서 전환 시 override 하여 단일 위치에서 발생하는 모든 전환을 관찰할 수 있음</p>
<pre><code class="language-dart">class SimpleBlocObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print(&#39;${bloc.runtimeType} $change&#39;);
  }

  @override
  void onTransition(Bloc bloc, Transition transition) {
    super.onTransition(bloc, transition);
    print(&#39;${bloc.runtimeType} $transition&#39;);
  }

  @override
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    print(&#39;${bloc.runtimeType} $error $stackTrace&#39;);
    super.onError(bloc, error, stackTrace);
  }
}

void main() {
  BlocOverrides.runZoned(
    () {
      CounterBloc()
        ..add(CounterIncrementPressed())
        ..close();
    },
    blocObserver: SimpleBlocObserver(),
  );
}

Transition { currentState: 0, event: Increment, nextState: 1 }
CounterBloc Transition { currentState: 0, event: Increment, nextState: 1 }
Change { currentState: 0, nextState: 1 }
CounterBloc Change { currentState: 0, nextState: 1 }

// onTransition 먼저 호출 (글로벌 이전 로컬) / 그 다음에 onChange 호출

// bloc instance의 또 다른 고유한 특징은 새로운 이벤트가 block 추가 시 호출되는 event 재정의할 수 있음 
// onEvent도 전역뿐만 아니라 로컬에서 재정의할 수 있음

abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc&lt;CounterEvent, int&gt; {
  CounterBloc() : super(0) {
    on&lt;CounterIncrementPressed&gt;((event, emit) =&gt; emit(state + 1));
  }

  @override
  void onEvent(CounterEvent event) {
    super.onEvent(event);
    print(event);
  }

  @override
  void onChange(Change&lt;int&gt; change) {
    super.onChange(change);
    print(change);
  }

  @override
  void onTransition(Transition&lt;CounterEvent, int&gt; transition) {
    super.onTransition(transition);
    print(transition);
  }
}

class SimpleBlocObserver extends BlocObserver {
  @override
  void onEvent(Bloc bloc, Object? event) {
    super.onEvent(bloc, event);
    print(&#39;${bloc.runtimeType} $event&#39;);
  }
  // onEvent는 이벤트가 추가되는 즉시 호출 // local onEvent는 blocObserver의 global onEvent 앞에 호출

  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print(&#39;${bloc.runtimeType} $change&#39;);
  }

  @override
  void onTransition(Bloc bloc, Transition transition) {
    super.onTransition(bloc, transition);
    print(&#39;${bloc.runtimeType} $transition&#39;);
  }
}

Increment
CounterBloc Increment
Transition { currentState: 0, event: Increment, nextState: 1 }
CounterBloc Transition { currentState: 0, event: Increment, nextState: 1 }
Change { currentState: 0, nextState: 1 }
CounterBloc Change { currentState: 0, nextState: 1 }</code></pre>
<h4 id="error-handling">Error Handling</h4>
<p>bloc에는 addError와 onError 메서드가 있음 / bloc 내부 어디에서나 addError를 호출하여 오류 발생 나타낼 수 있음 =&gt; 오류 재정의하여 오류 대응 가능</p>
<pre><code class="language-dart">
abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc&lt;CounterEvent, int&gt; {
  CounterBloc() : super(0) {
    on&lt;CounterIncrementPressed&gt;((event, emit) {
      addError(Exception(&#39;increment error!&#39;), StackTrace.current);
      emit(state + 1);
    });
  }

  @override
  void onChange(Change&lt;int&gt; change) {
    super.onChange(change);
    print(change);
  }

  @override
  void onTransition(Transition&lt;CounterEvent, int&gt; transition) {
    print(transition);
    super.onTransition(transition);
  }

  @override
  void onError(Object error, StackTrace stackTrace) {
    print(&#39;$error, $stackTrace&#39;);
    super.onError(error, stackTrace);
  }
}

// local onError 먼저 호출 =&gt; blocObserver에서 글로벌 onError 호출
// onError 및 onChange는 bloc 및 cubit instance 모두 동일한 방식에서 작동
// eventHandler 내에서 처리되지 않은 예외도 onError로 보고 됌
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[dart bloc (basic / cubit)]]></title>
            <link>https://velog.io/@e_maker/dart-bloc-basic-cubit</link>
            <guid>https://velog.io/@e_maker/dart-bloc-basic-cubit</guid>
            <pubDate>Tue, 07 Jun 2022 07:53:54 GMT</pubDate>
            <description><![CDATA[<p>Bloc 사용 시 비즈니스 로직에서 presentation을 분리하기 쉽게 만듬! 코드를 빠르고 쉽게 테스트할 수 있고, 재사용할 수 있음</p>
<p>데이터 기반 의사결정 가능 =&gt; 단일 유저 상호작용을 기록하기에!!</p>
<p>가능한 한 효율적으로 작업하고 어플리케이션 내에서와 다른 어플리케이션에서 구성 요소 재사용!!</p>
<p>bloc의 3요소
simple : 이해하기 쉽고 다양한 기술 수즌을 가진 것을 사용할 수 있음
Powerful: 더 작은 구서용소로 구성 / 복잡한 응용프로그램 만드는데 도움!!
Testable: 어플리케이션의 모든 측면을 쉽게 test =&gt; 반복할 수 있음!!</p>
<h4 id="stream-비동기-데이터의-시퀀스">stream (비동기 데이터의 시퀀스)</h4>
<p>(익숙치 않다면 streams =&gt; 물이 흐르는 파이프에서 파이프 = stream , 물은 비동기 데이터)</p>
<pre><code class="language-dart">Stream&lt;int&gt; countStream(int max) async* {
    for (int i = 0; i &lt; max; i++) {
        yield i;
    }
}

Future&lt;int&gt; sumStream(Stream&lt;int&gt; stream) async {
    int sum = 0;
    await for (int value in stream) {
        sum += value;
    }
    return sum;
}

void main() async {
    /// Initialize a stream of integers 0-9
    Stream&lt;int&gt; stream = countStream(10);
    /// Compute the sum of the stream of integers
    int sum = await sumStream(stream);
    // stream 과 sum이 돌아가면서 출력되고 마지막 sum이 print 된다
    print(sum); // 45
}</code></pre>
<hr>
<h3 id="cubit-blocbase를-상속받는-class--state의-상태를-관리할-수-있음">cubit (blocbase를 상속받는 class / state의 상태를 관리할 수 있음)</h3>
<p><img src="https://i.imgur.com/4hpVhtE.png" alt="cubit"></p>
<p>state 변경의 트리거가 적용될 수 있도록 함수를 노출할 수 있음
상태는 큐빗의 출력값과 어플리케이션의 상태 중 하나를 나타낸다.
UI 컴포넌트는 상태를 통지받고 현재 상태에 따라 자신의 일부를 다시 그릴 수 있음!!</p>
<pre><code class="language-dart">class CounterCubit extends Cubit&lt;int&gt; {
  CounterCubit() : super(0);
}
// Cubit의 스테이트 타입을 정의할 필요가 있음!, 
// 복잡한 상황에서는 필요에 의해서 class 대신에 
// primitive types(int,double,String, bool, dynamic)을 사용할 수 있음!!

class CounterCubit extends Cubit&lt;int&gt; {
  CounterCubit(int initialState) : super(initialState);
}

// creating a cubit / state type 정의할 필요가 있음!  =&gt; 
//cubit 관리 / counterCubit 상태를 나타내는 것은 int 더 복잡할 떄는 class 사용 ! / 
//initialstate하게 설정할 수 있지만, 외부의 값을 받아드린다.

final cubitA = CounterCubit(0); // state starts at 0
final cubitB = CounterCubit(10); // state starts at 10

</code></pre>
<p>cubit의 상태 변경 (각각의 큐빗은 emit이라는 것으로 새로운 상태를 출력 가능!)</p>
<pre><code class="language-dart">class CounterCubit extends Cubit&lt;int&gt; {
  CounterCubit() : super(0);

  void increment() =&gt; emit(state + 1);
  //외부로 호출하여 전달가능 / counterCubit 상태를 증가 시킴 / 이 메서드는 내부에서만 사용해야함

}
void main() {
  final cubit = CounterCubit();
  print(cubit.state); // 0
  cubit.increment();
  print(cubit.state); // 1
  cubit.close();
}

Future&lt;void&gt; main() async {
  final cubit = CounterCubit();
  final subscription = cubit.stream.listen(print); // 1
  //호출 시 이후의 상태 변경만 수신
  cubit.increment();
  await Future.delayed(Duration.zero);
  //서브스크립션이 즉시 취소되지 않도록 추가 
  await subscription.cancel();
  await cubit.close();
}

//subscription을 실행 =&gt; 각 상태 변경시 print 호출 / 그리고 명령어 호출! =&gt; 새로운 상태를 방출하는 기능, cancel</code></pre>
<p>cubit (새로운 상태가 emit 될 때, change가 발생함 / observe 시킬 수 있음 =&gt; cubit에 변화를 주면서, onchange를 overriding 시킴 )</p>
<p>BlocObserver (블록 라이브러리를 사용하면, 한 곳에서 모든 변경사항을 엑세슬 할 수 있음! / 대규모 어플리케이션에서느 상당히 좋음!)</p>
<pre><code>
BlocObserver (블록 라이브러리를 사용하면, 한 곳에서 모든 변경사항을 엑세슬 할 수 있음! / 대규모 어플리케이션에서느 상당히 좋음!)

```dart
class SimpleBlocObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print(&#39;${bloc.runtimeType} $change&#39;);
  }
}
//  모든 변화에 대응하여 무언가를 할 수 있다면 blocObserver를 사용할 수 있음!!
// blocObserver를 확장하고 onChange 메서드를 재정의하기만 하면 됌!

void main() {
  BlocOverrides.runZoned(
    () {
      CounterCubit()
        ..increment()
        ..close();
    },
    blocObserver: SimpleBlocObserver(),
  );
}

Change { currentState: 0, nextState: 1 }
CounterCubit Change { currentState: 0, nextState: 1 }
// 내부 onchange 재정의가 먼저 호출된 후 blockObserver에서 onchange가 호출!</code></pre><p>Error Handling (큐빗에는 오류가 발생했음을 나타내는 데 사용할 수 있는 addError 메서드가 있음)</p>
<pre><code class="language-dart">class CounterCubit extends Cubit&lt;int&gt; {
  CounterCubit() : super(0);

  void increment() {
    addError(Exception(&#39;increment error!&#39;), StackTrace.current);
    emit(state + 1);
  }

  @override
  void onChange(Change&lt;int&gt; change) {
    super.onChange(change);
    print(change);
  }

  @override
  void onError(Object error, StackTrace stackTrace) {
    print(&#39;$error, $stackTrace&#39;);
    super.onError(error, stackTrace);
  }

  //onError는 큐빗 내에서 재정의하여 특정 큐빗에 대한 모든 오류를 처리할 수 있습니다.
  //onError를 BlockObserver에서 재정의하여 보고된 모든 오류를 전체적으로 처리할 수도 있음
}

class SimpleBlocObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print(&#39;${bloc.runtimeType} $change&#39;);
  }

  @override
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    print(&#39;${bloc.runtimeType} $error $stackTrace&#39;);
    super.onError(bloc, error, stackTrace);
  }
}
//onChange와 마찬가지로 내부 onError 재정의는 글로벌 BlocObserver 재정의 전에 호출</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[flutter freeze (2)]]></title>
            <link>https://velog.io/@e_maker/flutter-freeze-2</link>
            <guid>https://velog.io/@e_maker/flutter-freeze-2</guid>
            <pubDate>Mon, 06 Jun 2022 11:53:21 GMT</pubDate>
            <description><![CDATA[<h2 id="freezed">Freezed</h2>
<p>freezed에서 getter나 void 함수를 그냥 쓰려고하면 오류가 걸린다. 그래서 아래처럼 사용하면 된다.</p>
<pre><code class="language-dart">// 생략
const Person._();
  // 밑의 get과 hello를 쓰기 위해 이친구를 사용함 / 그리고 코드에 오류가 발생하기에 다시 build를 해줘야함
  // define a private empty constructor:

  get nameLength =&gt; this.name.length;

  void hello() {
    print(&quot;123&quot;);
  }
</code></pre>
<p>다른 클래스로 묶어서 사용하기..</p>
<pre><code class="language-dart">@freezed
class Group with _$Group {
  factory Group({
    required int id,
    required String name,
    required School school,
  }) = _Group;
}

@freezed
class School with _$School {
  factory School({
    required int id,
    required String name,
  }) = _School;
}</code></pre>
<p>그리고 만약 무효하여 쓰고 싶다면 null을 이용한다.</p>
<pre><code class="language-dart">Company company = Company(name: &#39;Google&#39;, director: Director(assistant: null));</code></pre>
<p>기본값 (다트는 factory 생성자 리디렉션이 기본값을 허용하는 것을 원치 않음!
방법은 예제처럼 하기</p>
<pre><code class="language-dart">class Example with _$Example {
  const factory Example([@Default(42) int value]) = _Example;
  //@default 사용
}
// 직렬화/역직렬화를 사용하는 경우 자동으로 추가됩니다 @JsonKey(defaultValue: &lt;something&gt;).
</code></pre>
<p>사용하지 않을려는 것은 @deprecated을 선언하여 수행</p>
<pre><code class="language-dart">@freezed
class Person with _$Person {
  @deprecated
  const factory Person({
    String? name,
    int? age,
    Gender? gender,
  }) = _Person;
}</code></pre>
<p>union (공통으로 쓰이는 것들을 공유해서 공통이 아닌 것은 기능 잃음)</p>
<pre><code class="language-dart">@freezed
class Person with _$Person {
  //(2)

  factory Person({
    required int id,
    required String name,
    required int age,
    int? statusCode,
  }) = _Person;
// factory Person.data(int value) = _Data // 데이터
  factory Person.loading({int? statusCode}) = _Loading; // 로딩

  factory Person.error(String message, {int? statusCode}) = _Error; // 에러 
}</code></pre>
<p>union.when (패턴 일치를 소멸시키는 기능함 = 공식문서 참고)</p>
<pre><code class="language-dart">mapWhen(Person person) {
    return person.when(
        (id, name, age, statusCode) =&gt;
            &#39;id: $id, name: $name, age: $age, statusCode: $statusCode&#39;,
        loading: (int? statusCode) =&gt; &#39;loading...&#39;,
        error: (String message, int? statusCode) =&gt; message);
  }

   renderText(&#39;person&#39;, person.toString()),
                renderText(&#39;personLoading&#39;, personLoading.toString()),
                renderText(&#39;personError&#39;, personError.toString()),
                renderText(&#39;personcode&#39;, person.statusCode.toString()),
                // renderText(&#39;person&#39;, person.id), 맨 위 객체에선 statusCode만 공통되기에 에러 발생
                renderText(&#39;person.when&#39;, mapWhen(person)),
                renderText(&#39;personLoading.when&#39;, mapWhen(personLoading)),
                renderText(&#39;personError.when&#39;, mapWhen(personError)),</code></pre>
<p>공식문서 예시</p>
<pre><code class="language-dart">@freezed
class Model with _$Model {
  factory Model.first(String a) = First;
  factory Model.second(int b, bool c) = Second;
}

var model = Model.first(&#39;42&#39;);

print(
  model.when(
    first: (String a) =&gt; &#39;first $a&#39;,
    second: (int b, bool c) =&gt; &#39;second $b $c&#39;
  ),
); // first 42</code></pre>
<p>Map (when 과 비슷.. 구조화는 시키지 않음</p>
<pre><code class="language-dart">var model = Model.first(&#39;42&#39;);

print(
  model.map(
    first: (First value) =&gt; &#39;first ${value.a}&#39;,
    second: (Second value) =&gt; &#39;second ${value.b} ${value.c}&#39;
  ),
); // first 42
var model = Model.second(42, false)
print(
  model.map(
    first: (value) =&gt; value,
    second: (value) =&gt; value.copyWith(c: true),
  )
); // Model.second(b: 42, c: true)
</code></pre>
<p>is / as를 사용하여 Freezed class read</p>
<pre><code class="language-dart">void main() {
  Example value;

  if (value is Person) {
    // By using `is`, this allows the compiler to know that &quot;value&quot; is a Person instance
    // and therefore allows us to read all of its properties.
    print(value.age);
    value = value.copyWith(age: 42);
  }

  // Alternatively we can use `as` if we are certain of type of an object:
  Person person = value as Person;
  print(person.age);
}</code></pre>
<p>implements / @with 이용한 공용 유형 개별클래스mixin / interface</p>
<pre><code class="language-dart">// 동일한 클래스에 여러 유형이 있는 경우 하나를 만들어 인터페이스 구현 /클래스 혼합 가능
abstract class GeographicArea {
  int get population;
  String get name;
}

@freezed
class Example with _$Example {
  const factory Example.person(String name, int age) = Person;

  @Implements&lt;GeographicArea&gt;()
  const factory Example.city(String name, int population) = City;
}</code></pre>
<p>제네릭 믹스인 또는 인터페이스를 지정하려는 경우 
With.fromString생성자 를 사용하여 Implements.fromString각각 문자열로 선언</p>
<pre><code class="language-dart">// 모든 추상 멤버를 구현하여 인터페이스 요구 사항을 준수하는지 확인
// 고정 클래스 에는 @With/ 를 사용할 수 없습니다 . @Implements고정 클래스는 확장하거나 구현할 수 없습니다.
abstract class GeographicArea {}
abstract class House {}
abstract class Shop {}
abstract class AdministrativeArea&lt;T&gt; {}

@freezed
class Example with _$Example {
  const factory Example.person(String name, int age) = Person;

  @With&lt;AdministrativeArea&lt;House&gt;&gt;()
  const factory Example.street(String name) = Street;

  @With&lt;House&gt;()
  @Implements&lt;Shop&gt;()
  @Implements&lt;GeographicArea&gt;()
  const factory Example.city(String name, int population) = City;
}</code></pre>
<p>FromJson /ToJson
json_serializable 패키지를 이용해서 만듬.
1.part &#39;{name}.g.dart&#39;; 을 선언 
2. factory {name}.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _${name}FromJson(json); 선언
Freezed는 팩토리에서 를 사용하는 경우에만 fromJson을 생성</p>
<p>만약 여러 생성자가 있는 클래스에서 fromJson을 사용한다면. runtimeType을 이용해서 사용할 생성자 선택(공식문서 참조)</p>
<pre><code class="language-dart">@freezed
class MyResponse with _$MyResponse {
  const factory MyResponse(String a) = MyResponseData;
  const factory MyResponse.special(String a, int b) = MyResponseSpecial;
  const factory MyResponse.error(String message) = MyResponseError;

  factory MyResponse.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$MyResponseFromJson(json);
}

[
  {
    &quot;runtimeType&quot;: &quot;default&quot;,
    &quot;a&quot;: &quot;This JSON object will use constructor MyResponse()&quot;
  },
  {
    &quot;runtimeType&quot;: &quot;special&quot;,
    &quot;a&quot;: &quot;This JSON object will use constructor MyResponse.special()&quot;,
    &quot;b&quot;: 42
  },
  {
    &quot;runtimeType&quot;: &quot;error&quot;,
    &quot;message&quot;: &quot;This JSON object will use constructor MyResponse.error()&quot;
  }
]

@Freezed(unionKey: &#39;type&#39;, unionValueCase: FreezedUnionCase.pascal)
class MyResponse with _$MyResponse {
  const factory MyResponse(String a) = MyResponseData;

  @FreezedUnionValue(&#39;SpecialCase&#39;)
  const factory MyResponse.special(String a, int b) = MyResponseSpecial;

  const factory MyResponse.error(String message) = MyResponseError;

  // ...
}

[
  {
    &quot;type&quot;: &quot;Default&quot;,
    &quot;a&quot;: &quot;This JSON object will use constructor MyResponse()&quot;
  },
  {
    &quot;type&quot;: &quot;SpecialCase&quot;,
    &quot;a&quot;: &quot;This JSON object will use constructor MyResponse.special()&quot;,
    &quot;b&quot;: 42
  },
  {
    &quot;type&quot;: &quot;Error&quot;,
    &quot;message&quot;: &quot;This JSON object will use constructor MyResponse.error()&quot;
  }
]</code></pre>
<p>다른 yaml 파일을 만들어서 선언할수도 있음!</p>
<p>JSON 응답을 제어하지 않는 경우 사용자 지정변환기 구현 가능(사용자 지정 변환기는 사용할 생성자를 결정하기 위한 자체 논리를 구현)</p>
<pre><code class="language-dart">class MyResponseConverter implements JsonConverter&lt;MyResponse, Map&lt;String, dynamic&gt;&gt; {
  const MyResponseConverter();

  @override
  MyResponse fromJson(Map&lt;String, dynamic&gt; json) {
    // type data was already set (e.g. because we serialized it ourselves)
    if (json[&#39;runtimeType&#39;] != null) {
      return MyResponse.fromJson(json);
    }
    // you need to find some condition to know which type it is. e.g. check the presence of some field in the json
    if (isTypeData) {
      return MyResponseData.fromJson(json);
    } else if (isTypeSpecial) {
      return MyResponseSpecial.fromJson(json);
    } else if (isTypeError) {
      return MyResponseError.fromJson(json);
    } else {
      throw Exception(&#39;Could not determine the constructor for mapping from JSON&#39;);
    }
 }

  @override
  Map&lt;String, dynamic&gt; toJson(MyResponse data) =&gt; data.toJson();
}

@freezed
class MyModel with _$MyModel {
  const factory MyModel(@MyResponseConverter() MyResponse myResponse) = MyModelData;

  factory MyModel.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$MyModelFromJson(json);
}

@freezed
class MyModel with _$MyModel {
  const factory MyModel(@MyResponseConverter() List&lt;MyResponse&gt; myResponse) = MyModelData;

  factory MyModel.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$MyModelFromJson(json);
}</code></pre>
<p>freezed 다양한 매개변수 출력 변경 </p>
<pre><code class="language-dart">@Freezed(
  // Disable the generation of copyWith/==
  copyWith: false,
  equal: false,
)
class Person with _$Person {...}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[flutter freezed (1)]]></title>
            <link>https://velog.io/@e_maker/flutter-freezed-1</link>
            <guid>https://velog.io/@e_maker/flutter-freezed-1</guid>
            <pubDate>Mon, 06 Jun 2022 11:24:12 GMT</pubDate>
            <description><![CDATA[<h2 id="freezed">freezed</h2>
<p>code generator 기능을 함 (data-classes / unions / pattern-matching / cloning</p>
<p>역할</p>
<p>생성자 + 속성 / Equatable / copyWith / (de/serialization handling) 기능을 해줌</p>
<hr>
<p>설치방법</p>
<pre><code>$ flutter pub add freezed_annotation
// tool for code-generator run  
$ flutter pub add --dev build_runner
// code generator
$ flutter pub add --dev freezed
// freezed를 위한 annotations contains packages !!
// 만약 json_serializable:이 필요하면 pubspec.yaml에 dev_dependencies에 추가</code></pre><p>flutter pub run build_runner build을 이용해서 파일 빌드를 해서 자동으로 freezed 파일이 만들어지게 해줌! (import 이용)</p>
<p>변경 시에도 flutter pub run build_runner build를 이용해서 재빌드 시키면 됌</p>
<hr>
<p>model 만들기
(import &#39;package:flutter/foundation.dart&#39;를 이용해서 flutter devtool에서 객체를 잘 읽을 수 있게 해주니 나쁘지 않음!!)</p>
<p>예시를 통해 이해해보기 </p>
<pre><code class="language-dart">
import &#39;package:freezed_annotation/freezed_annotation.dart&#39;;

part &#39;person.freezed.dart&#39;;

// 꼴랑 이코드만 추가함
@freezed // freezed 선언
class Person with _$Person {
// mixin을 이용하여 객체의 다양한 속성/메소드 정의
  //(2)

  @Assert(&#39;name.isNotEmpty&#39;, &#39;name cannot be empty&#39;)
  // validation을 위해 쓰임  / 앞의 것이 허용되지 않으면 뒤에 에러가 나옴
  @Assert(&#39;age &gt;= 0&#39;)
  factory Person({
  // factory로 선언 / 따른 모델처럼 final로 위의 선언하지 않아도 문제 x
  // 생성자 정의시 표시된 대로 키워드 사용(const로 만들어도 되긴함..)

    required int id,
    required String name,
    required int age,
    required Group group,
  }) = _Person;

  // factory Person.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$PersonFromJson(json);
  // json으로도 만들 수 있음! 

  const Person._();
  // 밑의 get과 hello를 쓰기 위해  사용함 / 그리고 코드에 오류가 발생하기에 다시 build를 해줘야함
  // define a private empty constructor:

  get nameLength =&gt; this.name.length;

  void hello() {
    print(&quot;123&quot;);
  }
}

</code></pre>
<p>변경 가능한 속성을 정의하고 싶다면, @unfreezed를 사용</p>
<pre><code class="language-dart">
@unfreezed
class Person with _$Person {
  factory Person({
    required String firstName,
    required String lastName,
    required final int age,
  }) = _Person;

  factory Person.fromJson(Map&lt;String, Object?&gt; json)
      =&gt; _$PersonFromJson(json);
}

void main() {
  var person = Person(firstName: &#39;John&#39;, lastName: &#39;Smith&#39;, age: 42);

  person.firstName = &#39;Mona&#39;;
  person.lastName = &#39;Lisa&#39;;
}
// age속성을 으로 명시적으로 표시했기 때문에 여전히 변경할 수 없습니다 final.

// Person더 이상 사용자 정의 ==/hashCode 구현이 없습니다.</code></pre>
<p>이제 이부분은 ui에 적용해보겠습니다.</p>
<pre><code class="language-dart">    final schoolOne = School(id: 3, name: &#39;MIT&#39;);
    final groupOne =
        Group(id: 2, name: &#39;Flutter Development&#39;, school: schoolOne);
    final personOne = Person(id: 1, name: &#39;sunny&#39;, age: 29, group: groupOne);

    final personNew = personOne.copyWith(
      group: groupOne.copyWith(
        school: schoolOne.copyWith(
          name: &#39;Yale&#39;,
        ),
      ),
    );
    final personNewTwo = personOne.copyWith.group.school(name: &#39;Harvard&#39;);

    final personTwo = Person(id: 1, name: &#39;sunny&#39;, age: 29, group: groupOne);
    final personThree = Person(
        id: personOne.id, name: personOne.name, age: 18, group: groupOne);
    final personFour = personOne.copyWith(age: 18);

    // personOne.hello();

    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: true,
        title: const Text(&quot;freezed test&quot;),
        centerTitle: true,
      ),
      body: SafeArea(
        child: SingleChildScrollView(
          child: Container(
            padding: const EdgeInsets.only(left: 10, right: 10),
            child: Column(
              children: [
                renderText(&#39;person1.id&#39;, personOne.id.toString()),
                renderText(&#39;person1.name&#39;, personOne.name),
                renderText(&#39;person1.age&#39;, personOne.age.toString()),
                renderText(&#39;toString()&#39;, personOne.toString()),
                // renderText(&#39;toJson()&#39;, personOne.toJson().toString()),
                renderText(&#39;==&#39;, (personOne == personTwo).toString()),
                renderText(&#39;nameLength&#39;, personOne.nameLength.toString()),
                renderText(&#39;person4.ToString()&#39;, personFour.toString()),
                renderText(&#39;personNew.ToString()&#39;, personNew.toString()),
                renderText(&#39;personNewTwo.ToString()&#39;, personNewTwo.toString()),
              ],
            ),
          ),
        ),
      ),
    );

   renderText(String title, String text) {
    return Column(
      children: [
        Row(
          children: [
            Expanded(
              child: Text(
                title,
                style: const TextStyle(
                    fontSize: 20.0, fontWeight: FontWeight.bold),
              ),
            ),
          ],
        ),
        Row(
          children: [
            Expanded(
              child: Text(
                text,
                style: const TextStyle(fontSize: 20.0),
              ),
            ),
          ],
        ),
        const Divider(
          height: 1,
        ),
        const SizedBox(
          height: 10,
        )
      ],
    );
  }</code></pre>
<p>위에 예제에서 copyWith 부터 설명하자면..</p>
<p>copyWth은 개체를 복사하는데 사용하며, 기존에 있던 값은 건드리지 않고 새로운 값만 변경시켜 사용함!
그렇지만 복잡한 개체에서 불편할 수 있기에 아래와 같은 코드를 씁니다.</p>
<pre><code class="language-dart">Company company;

Company newCompany = company.copyWith(
  director: company.director.copyWith(
    assistant: company.director.assistant.copyWith(
      name: &#39;John Smith&#39;,
    ),
  ),
);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Provider Study 2-3]]></title>
            <link>https://velog.io/@e_maker/Provider-Study-2-3</link>
            <guid>https://velog.io/@e_maker/Provider-Study-2-3</guid>
            <pubDate>Sun, 05 Jun 2022 00:19:00 GMT</pubDate>
            <description><![CDATA[<h3 id="statenotifier--provider">StateNotifier / Provider</h3>
<p>어제 스터디에서 이부분을 설명을 잘해주셨지만, 뭔가 디테일 하게 정리를 해야 할 거 같은 기분이 들어.. 정리를 해둔다</p>
<p>다른 예제처럼 state를 먼저 만들었다.</p>
<pre><code class="language-dart">class BgColorState extends Equatable {
  final Color color;

  BgColorState({required this.color});

  @override
  List&lt;Object?&gt; get props =&gt; [color];

  @override
  bool get stringify =&gt; true;

  BgColorState copyWith({
    Color? color,
  }) {
    return BgColorState(color: color ?? this.color);
  }
}</code></pre>
<hr>
<p>색상을 변경시키는 state를 만들었다면 이제 stateNotifier를 설명해보겠다
에제를 통해 하나씩 설명해보겠다.</p>
<pre><code class="language-dart">class BgColor extends StateNotifier&lt;BgColorState&gt; {
// state type을 정하기에 문제 방지도 되고 좋음!
//초기 state가 명확한 점!!
  BgColor() : super(BgColorState(color: Colors.blue));
 // initial state setting은 super call에 주는 값이 초기의 state임!  
 // BgColorState를 선언하고 color 값을 할당함!





   void changeColor() {
   // 초기의 state 값을 외부에서 바꿔줘야한다면, bgcolor에 state를 전달하고 그 값을 이용해 state setting 해야함
    if (state.color == Colors.blue) {
      state = state.copyWith(color: Colors.black);
        // state라 하는 변수의 statenotifier에 state 값이 저장된다는 것임
       // state 변수를 통해서, state를 access, setting 할 수 있음
    } else if (state.color == Colors.black) {
      state = state.copyWith(color: Colors.red);
    } else {
      state = state.copyWith(color: Colors.blue);
    }
  }
}
</code></pre>
<p>코드에 설명을 해놨지만, 난잡하기에 다시 설명하자면,</p>
<ol>
<li>초기에 state를 설정한다. </li>
<li>copyWith를 통해 bgColorState에 color를 state를 통해 바꿔준다. (state를 통해 자동적(?)으로 바꿔주기에! notifyListener를 통해 바꿔줄 필요가 없다!</li>
<li>statenotifier에 tate를 명확하게 설정하기에 state type이 명확해진다.</li>
<li>statenotifier에는 state 값이 저장된다.</li>
<li>state 변수를 통해, state를 access하거나 setting 할 수 있다는 것이다.</li>
</ol>
<p>개인적으로 proxyprovider처럼 여러가지를 선언하지 않고도 편하게 쓸 수 있다는 것이 좋아보인다.</p>
<hr>
<h4 id="그렇다면-locator는-뭘까요">그렇다면 locator는 뭘까요?</h4>
<p>stateNotifier에서 read, watch 기능을 사용하기 위한 것으로 locatorMixin으로 mixin 시키며, serviceLocator라고도 부름!</p>
<h4 id="어떻게사용할까">어떻게사용할까?</h4>
<p>state는 위에서 처럼 만드니 counter 사용하는 부분에서 예를 들어보겠다.</p>
<pre><code class="language-dart">class Counter extends StateNotifier&lt;CounterState&gt; with LocatorMixin {
  // read, watch 기능을 사용하기 위한 것 LocatorMixin =&gt; serviceLocator라고도 함
  Counter() : super(CounterState(counter: 0));

  void increment() {
    print(read&lt;BgColor&gt;().state);
    // 이벤트 핸들러에서 watch를 계속 써야할 필요가 없으므로 read로 쓰기

    Color currentColor = read&lt;BgColor&gt;().state.color;
    // 다른 state color를 read 하기 위해 사용!

    if (currentColor == Colors.black) {
      state = state.copyWith(counter: state.counter + 10);
    } else if (currentColor == Colors.red) {
      state = state.copyWith(counter: state.counter - 10);
    } else {
      state = state.copyWith(counter: state.counter + 1);
    }
  }

  @override
  // LocatorMixin에서 상속받은 update 사용
  void update(Locator watch) {

    print(&#39;in Counter StateNtifier: ${watch&lt;BgColorState&gt;().color}&#39;);
    print(&#39;in Counter StateNtifier: ${watch&lt;BgColor&gt;().state.color}&#39;);

    super.update(watch);
  }
}
</code></pre>
<p>예시처럼 사용하면 될 것이다. 위에서 다룬 bgcolor랑 크게 다른 것이 없지만 update 부분을 간단히 설명하면</p>
<ol>
<li>다른 object의 update를 listening 해줌 (proxyProvider와 동일한 기능)</li>
<li>dependency가 없기에 외부 widget tree에서도 사용가능</li>
<li>update 내부에서는 read를 쓸 수 없기에, watch를 사용하여 변화를 반응 시켜준다.</li>
</ol>
<p>(proxyProvider를 사용할 때보다 코드도 간결해지고.. super.update(watch);를 이용하면 많이 스무스한 거 같다.)</p>
<hr>
<p>마지막은 배경색을 바꾸는 level이다</p>
<p>다른 부분은 동일했지만 여기선 counterstate의 counter를 사용하는데 선언을 final currentCounter = watch<CounterState>().counter; 으로 해서 무난하게 사용했다는 것이였다.</p>
<hr>
<p>그리고 사용되는 부분에서 statenotifierProvider를 multiProvider로 엮어서 순서에 맞게 설정하고</p>
<p>변수 선언 시에, 아래처럼 하면 된다.</p>
<pre><code class="language-dart"> final colorState = context.watch&lt;BgColorState&gt;();
 final counterState = context.watch&lt;CounterState&gt;();
 final levelState = context.watch&lt;Level&gt;();  </code></pre>
<p>개인적으로 proxyProvider보단 stateNotifier로 만들 것 같다. state로 만들기에 충돌 위험성도 적고... notifierListener를 사용하지 않아도 편하게 바뀌기에 실수도 적을 것 같다.</p>
<hr>
<p>추가 팁: state 변화시에 연관 되어있는 update 부분은 같이 재빌드가 되네..? ex) print를 찍어놓았을 경우에 그 부분이 모두 다시 출력되었음! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Provider Study 2-2]]></title>
            <link>https://velog.io/@e_maker/Provider-Study-2-2</link>
            <guid>https://velog.io/@e_maker/Provider-Study-2-2</guid>
            <pubDate>Sat, 04 Jun 2022 23:54:29 GMT</pubDate>
            <description><![CDATA[<h3 id="todo-app-make">Todo App Make</h3>
<ol>
<li>Equtable /  <a href="https://pub.dev/packages/equatable">공식문서</a></li>
</ol>
<p>Equatable 플러그인은 한 인스턴스와 다른 인스턴스가 같은 인스턴스인지 판단을 쉽게 할 수 있게 해주는 플러그인</p>
<p>(스터디 때 A님이 너무 설명을 잘해줘서 좋았던 거 같다..)</p>
<h4 id="why-need">why need?</h4>
<pre><code class="language-dart">@override
  bool operator ==(Object other) {
    return other is Person &amp;&amp; other.id == this.id 
      &amp;&amp; other.name == this.name 
      &amp;&amp; other.age == this.age;
  }

  @override
  int get hashCode {
    return this.id;
  }</code></pre>
<p>위의 예처럼 operator를 이용해서 1-2개를 비교하면 문제가 1도 없겠지만 .. 많이 사용되면 당연히 코드가 길어짐!
그러기에 우리는 equtable을 사용하면 좋음!</p>
<h4 id="how-use">how use?</h4>
<p>Equtable 클래스를 상속을 받고 props라는 메소드에 override 시켜주면 됌! ( 으잇!?)
예를 들어보쟈</p>
<pre><code class="language-dart">  @override
  List&lt;Object&gt; get props =&gt; [todos];
  // == 과 hashcode 함수를 생성하는데 사용 (이렇기에 todos만 같으면 두 props를 같다고 볼 수 있음)</code></pre>
<h4 id="예제에서-사용된-stringify에-대해-한번-더-생각해보자">예제에서 사용된 stringify에 대해 한번 더 생각해보자</h4>
<p>이 것에 대한 답은 스터디에서도 얻을 수 있었지만 공식 문서를 확인해보았습니다.</p>
<p>toString 구현을 위함! / toStringEquatable은 주어진 모든 props를 포함하는 메소드를 구현할 수 있음.</p>
<p>말이 애매하다면, 예를 하나 통해 설명해드렸습니다.</p>
<pre><code class="language-dart">
import &#39;package:equatable/equatable.dart&#39;;

class Person extends Equatable {
  const Person(this.name);

  final String name;

  @override
  List&lt;Object&gt; get props =&gt; [name];

  @override
  bool get stringify =&gt; true;
}

// name이 sunny라고 한다면 Person(sunny) =&gt; 기본 flag는 false 이며, toString에서는 Person 타입만 return 한다고 나와있네요..

// EquatableConfig.stringify = true; 를 이용해서 따로 선언할 수 있지만 새로 stringify가 들어온다면 stringify가 우선!!

// 결론 String 메서드을 구현하기 위해 사용한다.. (으잇!)</code></pre>
<p>나중에 활용하여 source Code를 정리해봐야긋다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Provider Study 2-1]]></title>
            <link>https://velog.io/@e_maker/Provider-Study-2-1</link>
            <guid>https://velog.io/@e_maker/Provider-Study-2-1</guid>
            <pubDate>Sat, 04 Jun 2022 23:39:30 GMT</pubDate>
            <description><![CDATA[<h4 id="provider-addlistener--removelistener">Provider addListener / removeListener</h4>
<hr>
<p>addListener는 객체가 변경될 때 호출될 콜백함수를 등록해준다는 것이 공식에서 나오는 부분이다. (말이 어렵게 나온 거 같기도..)</p>
<p>initstate에서 changenotifer를 사용하는 provider에서 addListener를 이용해서 함수를 등록한다고 생각해봅시다.
그리고 끝날때엔 removeListener를 이용해서 testCallListener를 제거해줘야 남아있지 않게 된다.</p>
<pre><code class="language-dart">@override
initState(){
 super.initState();
 testProvider.addListener(testCallListener);
}

// void callback Listener make!!
void testCallListener(){
print(&#39;test listener: {test.test}&#39;);
}

@override
  void dispose() {
    testProvider.removeListener(testCallListener);
    super.dispose();
  }
</code></pre>
<hr>
<h3 id="todo-app-rewind">todo App Rewind</h3>
<p>ChangeNotifierProxProvider&lt;a,b&gt; / 값이 바로 초기에 1번 업데이트 된다는 것이 제일 중요한게 아닌가 싶다. 예제를 통해 정리해둠</p>
<pre><code class="language-dart">ChangeNotifierProxyProvider&lt;TodoList, ActiveTodoCount&gt;(
          // todolist provider를 필요로 하므로 type으로 가져서 씀!
          // proxyprovider는 프록시 프로바이더가 create가 되고 업데이트가 바로 호출이 되므로,
          //값이 바로 업데이트 되어 기존에 initial이 0이나 []여도 문제가 없음
          create: (context) =&gt; ActiveTodoCount(
              initialActiveTodoCount:
                  context.read&lt;TodoList&gt;().state.todos.length),
          update: (
            BuildContext context,
            TodoList todoList,
            ActiveTodoCount? activeTodoCount,
          ) =&gt;
              activeTodoCount!..update(todoList),
        ),</code></pre>
<p>copyWith : 기존 값을 변하게 하지 않고 새로운 값을 만들 때 사용! (mutation 시키지 않게 하기 위해!?)
너무 어렵게 생각하면, 아마 어려운 친구가 되지 않을까 싶다.</p>
<p>```dart
  TodoFilterState copyWith({
    Filter? filter,
  }) {
    return TodoFilterState(filter: filter ?? this.filter);
  }
  // 새로 들어온 filter가 있다면 filter를 쓰고 없다면 기존 filter를 쓴다고 생각하면 쫌 쉽지 않을까?
``</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[extension]]></title>
            <link>https://velog.io/@e_maker/extension</link>
            <guid>https://velog.io/@e_maker/extension</guid>
            <pubDate>Fri, 03 Jun 2022 08:49:08 GMT</pubDate>
            <description><![CDATA[<h2 id="what-is-extension">what is extension?</h2>
<p>기존 라이브러리에 기능을 추가하기 위해 dart 2.7에 출시!</p>
<hr>
<p>예시들을 통해 살펴보자</p>
<pre><code class="language-dart">int.parse(&#39;42&#39;);

extension NumberParsing on String {
//  extension &lt;extension name&gt; on
//&lt;type&gt; { (&lt;member definition&gt;)* }
  int parseInt() {
    return int.parse(this);
  }

}
&#39;42&#39;.parseInt();</code></pre>
<pre><code class="language-dart">extension on DateTime{
  String get humanize{
    return &quot;${this.day}/${this.month}/${this.year}&quot;;
  }
}

extension on List&lt;int&gt; {
  int get sum =&gt; fold(0, (a, b) =&gt; a + b);
}

void main() {
  final dateTime = DateTime.now();
  print(dateTime.humanize);

  List&lt;int&gt; listInt = [1,2,3,4,5];
  print(listInt.sum);
}
</code></pre>
<p>프로젝트에서 다시 써보고, 소스코드로 정리를 깔끔하게 해봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stack / Positioned]]></title>
            <link>https://velog.io/@e_maker/Stack-Positioned</link>
            <guid>https://velog.io/@e_maker/Stack-Positioned</guid>
            <pubDate>Wed, 01 Jun 2022 11:31:13 GMT</pubDate>
            <description><![CDATA[<h2 id="stack">Stack</h2>
<p>중복이 가능한 위젯! / 위젯 리스트를 가지고 아래부터 형성 / 다른 위젯에 중첩으로 씌움 / 지정되지 않은 하위요소에 맞추고자하면 디폴트로 크기가 정해지기에 fit 속성을 사용할 수 있음</p>
<p>기본적으로는 topStart로 하위요소 정렬
Positioned를 사용해서 특정 하위요소의 특정 위치 지정 가능
overflow를 사용해서 경계선을 벗어나게 할 수도 있음</p>
<p>공식 예제
<img src="https://velog.velcdn.com/images/e_maker/post/43edc7f6-5665-4a5b-95a6-1bfa64179ef4/image.png" alt=""></p>
<pre><code class="language-dart">Stack(
  children: &lt;Widget&gt;[
    Container(
      width: 100,
      height: 100,
      color: Colors.red,
    ),
    Container(
      width: 90,
      height: 90,
      color: Colors.green,
    ),
    Container(
      width: 80,
      height: 80,
      color: Colors.blue,
    ),
  ],
)</code></pre>
<h2 id="positioned">Positioned</h2>
<p>위젯들을 stack에 배치할 때 위치를 조정할 때도 쓰임!
top,botton,left,light, width, height 등을 사용</p>
<p>position.fill을 사용해서 꽉 채울 수도 있음 (상당히 많이 쓴 기억이 있다.)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Expanded / Flexible]]></title>
            <link>https://velog.io/@e_maker/Expanded-Flexible</link>
            <guid>https://velog.io/@e_maker/Expanded-Flexible</guid>
            <pubDate>Wed, 01 Jun 2022 11:24:41 GMT</pubDate>
            <description><![CDATA[<h2 id="expanded--flexible">Expanded / Flexible</h2>
<p>두 위젯 모두 Column이나 Row에서만 쓰여야함
(flex의 child가 쓰이는 위젯)</p>
<h3 id="expanded-flexfittight">Expanded (FlexFit.tight)</h3>
<p>공식 홈페이지 따르면, 변경할 수 있는 것 중에서, 크기를 조절하기 위해 쓰인다는 내용이 있다.</p>
<p>여러 개가 쓰일 때는 flex를 높여서 쓰인다는 내용도 있다.</p>
<p>expanded는 프로젝트나 각종 부분에서 많이 쓰이지만 flex error를 항상 생각하고 써야했던 것 같다.</p>
<p>그리고 강제적으로 차지할 공간을 다 차지하려고 할 때 사용한다.</p>
<p>공식 홈페이지 예시는 아래와 같다.</p>
<pre><code class="language-dart">      body: Center(
        child: Row(
          children: &lt;Widget&gt;[
            Expanded(
              flex: 2,
              child: Container(
                color: Colors.amber,
                height: 100,
              ),
            ),
            Container(
              color: Colors.blue,
              height: 100,
              width: 50,
            ),
            Expanded(
              child: Container(
                color: Colors.amber,
                height: 100,
              ),
            ),
          ],
        ),
      ),

   //flex가 2인 친구가 더 큰 비율을 가짐</code></pre>
<h3 id="flexible">Flexible</h3>
<p>고정된 위젯은 쉽지만.. column이나 row의 상대적인 크기를 원할 때 쓰인다고 flutter는 소개한다.</p>
<p>flexible이 여러개 쓰이면.. flex를 사용해서 조절한다.</p>
<p>3가지가 flexible로 감싸있고 2,3,1이라면 2/6 , 3/6, 1/6으로 쓰인다고 생각하면 될 것이며, 고정된 친구들이 있다면 그 친구들이 먼저 길이가 잡힌다고 생각하자</p>
<p>추후에는 사이드로 만들면서 rewind가 아닌 실제 코드로 하나씩 쓸 예정...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SliverAppbar]]></title>
            <link>https://velog.io/@e_maker/SliverAppbar</link>
            <guid>https://velog.io/@e_maker/SliverAppbar</guid>
            <pubDate>Tue, 31 May 2022 13:26:49 GMT</pubDate>
            <description><![CDATA[<p>공식에서는 CustomScrollView와 사용하는 material Design Appbar라고 되어 있다. body에서 사용하고.. 유동적인 appBar로 생각하고 있다.</p>
<p><a href="https://api.flutter.dev/flutter/material/SliverAppBar-class.html">공식 홈페이지</a></p>
<p>예시를 통해 이해해보자</p>
<pre><code class="language-dart">return Scaffold(
body: CustomScrollView(
slivers: &lt;Widget&gt;[
SliverAppBar(
pinned: true,
// appbar를 스크롤 뷰의 시작부분에 visible 하게 할지 말지..
//snap: false,
// appbar snap 여부 / floating이 true 일떄만 적용
floating: false,
// 스크롤 할 시 보이게할 지 여부 
//stretch: true,
// 스크롤을 할때 appbar 크기가 늘어나는지..

expandedHeight: 300,
// appbar의 높이라고 생각하자( 최대..?)
 flexibleSpace: FlexibleSpaceBar(
 // appbar 하단에 보여지는 공간
                background: Stack(
                  children: [
                    Positioned.fill(
                      child: Image.network(
                        widget.imgLink,
                        fit: BoxFit.fill,
                        color: BogoColor.bogoOpacity,
                        colorBlendMode: BlendMode.modulate,
                        // blend를 해주면 그림자가 생기듯이 연해진다.
                      ),
                    ),

),
SliverList(
 delegate: SliverChildBuilderDelegate(
 // delegate에는 3가지 정도의 속성이 있으니 차후에 소개해드릴게요.
                      (BuildContext context, int index) {
                      return Column();},),
),
]),
);

</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TabBar / TabBarView]]></title>
            <link>https://velog.io/@e_maker/TabBar-TabBarView</link>
            <guid>https://velog.io/@e_maker/TabBar-TabBarView</guid>
            <pubDate>Tue, 31 May 2022 08:07:14 GMT</pubDate>
            <description><![CDATA[<h2 id="tabbar">TabBar</h2>
<p><img src="https://velog.velcdn.com/images/e_maker/post/73741d2f-1e69-4b9b-a59c-71f26857ecfe/image.png" alt=""></p>
<p>위와 같은 이미지처럼 사용!
class에 TickerProviderStateMixin을 믹스인 시킴!</p>
<p>예시를 통해 하나씩 보면</p>
<pre><code class="language-dart">class _HomePageState extends State&lt;HomePage&gt; with TickerProviderStateMixin {
  //TextEditingController homeSearchController = TextEditingController();
  TabController? tabController;
  @override
  void initState() {
    super.initState();
    tabController = TabController(vsync: this, length: 3);
  //length는 탭의 갯수라고 생각하자
  }
   TabBar(
       controller: tabController,
              tabs: [
                Tab(text: &quot;Movie&quot;),
                Tab(text: &quot;Drama&quot;),
                Tab(text: &quot;Animation&quot;),
              ],
              indicatorColor: BogoColor.bogoWhite,
              // 표시색상
            ),

 } </code></pre>
<p>위의 에처럼 쉽게 사용해볼 수 있다.</p>
<h2 id="tabbarview">TabBarView</h2>
<p>맨위 이미지처럼 movie, drama, animation을 클릭했을 때 하단에 나오는 View!</p>
<pre><code class="language-dart">TabBarView(
controller: tabController,
children: []),
//children 안에 개수는 tabbar의 개수와 같아야함</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Password TextField]]></title>
            <link>https://velog.io/@e_maker/Password-TextField</link>
            <guid>https://velog.io/@e_maker/Password-TextField</guid>
            <pubDate>Mon, 30 May 2022 13:37:03 GMT</pubDate>
            <description><![CDATA[<p>예시를 들어 TextField에서 password field를 만들어보자(rewind)</p>
<pre><code class="language-dart">
                TextField(
                  controller: passwordController,
                  scrollPadding: EdgeInsets.only(bottom: 120),
                  obscureText: _hideText,
                  // 글자를 숨키거나 보여주는 부분
                  style: TextStyle(color: BogoColor.bogoWhite),
                  decoration: InputDecoration(
                    hintText: &quot;비밀번호&quot;,
                    hintStyle: TextStyle(color: BogoColor.bogoWhite),
                    enabledBorder: 
                    // 밑줄 Border
                    UnderlineInputBorder(
                      borderSide: BorderSide(color: BogoColor.bogoGray),
                    ),
                    focusedBorder: UnderlineInputBorder(
                      borderSide: BorderSide(color: BogoColor.bogoGray),
                    ),
                    suffixIcon: IconButton(
                      color: BogoColor.bogoWhite,
                      icon: Icon(
                          _hideText ? Icons.visibility : Icons.visibility_off),
                      onPressed: () {
                        setState(() {
                          _hideText = !_hideText;
                        });
                      },
                    ),
                  ),
                ),

</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[flutter AppBar 분리]]></title>
            <link>https://velog.io/@e_maker/flutter-AppBar-%EB%B6%84%EB%A6%AC</link>
            <guid>https://velog.io/@e_maker/flutter-AppBar-%EB%B6%84%EB%A6%AC</guid>
            <pubDate>Mon, 30 May 2022 12:58:16 GMT</pubDate>
            <description><![CDATA[<p>appBar를 다른 파일로 정리하기 위해 사용했다!</p>
<pre><code class="language-dart">
class BogoAppbar extends StatefulWidget with PreferredSizeWidget {
  BogoAppbar({
    Key? key,
    required this.title,
    this.numberKey,
  }) : super(key: key);
  final String title;
  // dynamic controller;
  int? numberKey;

  @override
  Size get preferredSize =&gt; Size.fromHeight(52);
  // 무조건 있어야함!

  @override
  State&lt;BogoAppbar&gt; createState() =&gt; _BogoAppbarState();
}

class _BogoAppbarState extends State&lt;BogoAppbar&gt; {
  @override
  @override
  Widget build(BuildContext context) {
    String _searchtext;

    if (widget.numberKey == 1) {
      return AppBar(
        // centerTitle: true,
        leading: null,
        elevation: 0,
        title: Text(
          widget.title,
          style: TextStyle(
              color: BogoColor.bogoWhite, fontWeight: FontWeight.bold),
        ),
        actions: [],
      );
    } else if (widget.numberKey == 2) {
      return AppBar(
        // centerTitle: true,
        leading: null,
        elevation: 0,
        title: Text(
          widget.title,
          style: TextStyle(
              color: BogoColor.bogoWhite, fontWeight: FontWeight.bold),
        ),
        actions: [
          TextButton(
            child: Text(
              &quot;로그아웃&quot;,
              style: TextStyle(
                color: Colors.white,
              ),
            ),
            onPressed: () {
              // 로그아웃
              context.read&lt;AuthService&gt;().signOut();

              // 로그인 페이지로 이동
              Navigator.pushReplacement(
                context,
                MaterialPageRoute(builder: (context) =&gt; LoginPage()),
              );
            },
          ),
        ],
      );
    } else if (widget.numberKey == 3) {
      return AppBar(
        // centerTitle: true,
        leading: null,
        elevation: 0,
        title: Text(
          widget.title,
          style: TextStyle(
              color: BogoColor.bogoWhite, fontWeight: FontWeight.bold),
        ),
        actions: [
          TextButton(
            child: Text(
              &quot;로그아웃&quot;,
              style: TextStyle(
                color: Colors.white,
              ),
            ),
            onPressed: () {
              // 로그아웃
              context.read&lt;AuthService&gt;().signOut();

              // 로그인 페이지로 이동
              Navigator.pushReplacement(
                context,
                MaterialPageRoute(builder: (context) =&gt; LoginPage()),
              );
            },
          ),
        ],
      );
    }

  }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[flutter dotenv / WidgetFlutterBinding ]]></title>
            <link>https://velog.io/@e_maker/flutter-dotenv-WidgetFlutterBinding</link>
            <guid>https://velog.io/@e_maker/flutter-dotenv-WidgetFlutterBinding</guid>
            <pubDate>Mon, 30 May 2022 12:49:21 GMT</pubDate>
            <description><![CDATA[<h2 id="flutter-dotenv---widgetsflutterbindingensureinitialized">flutter dotenv /  WidgetsFlutterBinding.ensureInitialized</h2>
<p>flutter_dotenv는 다른 곳에 공유하면 안되는 api_key를 같은 것을 따로 저장한 파일을 읽어들일 수 있음!</p>
<p>flutter_dotenv: ^5.0.2 을 pubspec.yaml에 추가한다.</p>
<p>사용법은 아래의 예시처럼 사용한다. </p>
<pre><code class="language-dart">
import &#39;package:flutter_dotenv/flutter_dotenv.dart&#39;;

void main() async {
  await dotenv.load(fileName: &quot;.env&quot;);
  WidgetsFlutterBinding.ensureInitialized(); // main 함수에서 async 사용하기 위함

  runApp(
   child: const MyApp(),
  );
}
</code></pre>
<p> WidgetsFlutterBinding.ensureInitialized은 main 함수에서 비동기를 사용할 수 있게 해주므로 비동기를 한다면 필요함</p>
]]></description>
        </item>
    </channel>
</rss>