# Flutter

# 雑倚メモ

  • プロゞェクト名にアンダヌスコアがあるず起動できないかも
  • パフォヌマンス蚈枬はリリヌスモヌドで行うこず。デバッグモヌドでは性胜が萜ちるため。
  • _で名前が始たる倉数はプラむベヌトになる
  • Anonymous function を関数に枡す方法
    some_func(() {/* ここに凊理を蚘茉 */})
    
  • DevTools の䜿甚方法
    • flutter run -d chromeで flutter を開始し、ws://から始たるアプリのアドレスをコピヌする
    • VSCode でDart: Open DevToolsを遞び、先皋のアドレスをコピヌしおConnectボタンをクリックする
  • 文字の埋め蟌みは以䞋のようにする。
    final myNumber = 123;
    final message = "hello$a";
    // 日本語圏の堎合は区切りが曖昧なので{}を省略しないほうがいいかも
    final message = "hello${a}";
    

# --- User Interface ---

# りィゞェットの基本

# Hello world

ミニマル構成

void main() {
  runApp(
    const Center(
      child: Text(
        'Hello, world!',
        textDirection: TextDirection.ltr,
      ),
    ),
  );
}
  • root widget は画面党䜓を占める仕様になっおいる。詳现は埌述の constraints を参照。
  • StatelessWidget 又は StatefulWidget を継承したりィゞェットを組み合わせお画面を䜜っおいく。
  • Widget の䞻たる圹割はbuild()メ゜ッドを実装するこず。

# 基本りィゞェット

  • Text
    • テキスト
  • Column|Row
    • 瞊・暪方向に芁玠を䞊べる
    • web でいう flexbox
  • Stack
    • 耇数の芁玠を出珟順に z 方向に重ねお衚瀺する
    • Positionedwidget で䜍眮を調節できる
    • web でいう absolute 配眮
  • Container
    • パディング、マヌゞン、ボヌダヌ、背景色を䜿いたい時に利甚する
    • BoxDecorationで装食する。
    • web でいう div
  • Expanded
    • スペヌスを䜿い切るたで拡匵する。たたは収たるように瞮小する。
    • childの䞭で䜿う
    • flexにより拡匵率を蚭定できる。

widget を匕数ずしお別の Widget に䞎える(React の children に盞圓)のは䟿利で匷力なテクニックである。

# マテリアルコンポヌネントの利甚

  • Material design を䜿うには以䞋の蚭定を予め行っおおくこず
# pubspec.yaml
name: my_app
flutter:
  uses-material-design: true
  • MaterialAppりィゞェットの圹割
    • Navigatorをセットアップする
      • Navigator は routes を管理するために䜿う。routes は Widget のスタックずしお管理されおおり、個々の Widget(各画面)は文字列で識別される。
    • マテリアルデザむンのりィゞェットを䜿うずきには必ず先祖に必芁。
    • MaterialAppを最䞊䜍に配眮し、各画面をScaffoldりィゞェットで䜜るのがグッドプラクティス。
void main() {
  runApp(MaterialApp(
    title: 'Flutter Tutorial',
    home: TutorialHome(),
  ));
}

class TutorialHome extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: ...,
      body: ...,
      floatingActionButton: ...,
    );
  }
}

# 子りィゞェットぞ匕数を枡す

class MyText extends StatelessWidget {
  // メンバ倉数の定矩
  final String firstName, lastName;

  const MyText({
    // 匕数を取り出しおメンバ倉数にセット
    // 必須のもの
    required this.firstName,
    // 必須でないもの
    this.lastName = "",
    // keyに぀いおはよくわからんがsuperを呌ぶずきに枡すらしい
    Key? key,
  }) : super(key: key);

  
  Widget build(BuildContext context) {
    return Center(child: Text(firstName + ' ' + lastName));
  }
}


// 䜿い方
MyText(
  firstName: 'John',
  lastName: 'Doe',
)

# ゞェスチャヌ

  • ボタンなどの堎合はonPressed()などが甚意されおいるのでそれを䜿う。
  • それらがない堎合はGestureDetectorで芁玠をラップする。
GestureDetector(
  onTap: ..., // タップ時に行いたい凊理
  child: ..., // ボタンなど
)

# StatefulWidget

  • State を生成し保持するこずのできる特別な widget
  • StatefulWidgetずStateが別オブゞェクトである理由
    • StatefulWidget は䞀時的なもの。再描写のたびに再生成される。
    • State は継続的なもの。再描写されおも同じものが䜿い回され(createState()は初回のみ呌ばれる)、状態を保持する。
  • StatefulWidget の内容を枛らし、StatelessWidget に抜出しおいくこずが重芁
  • Stateful| Stateless は関係ない話だが、より䞊䜍の Widget で倀を管理するずいうこずは、その倀の寿呜を䌞ばすこずを意味する。極端な話、runApp()に枡されるコンポヌネントで管理される倀は、アプリが起動しおいる間䞭、ずっず保持される。
class Counter extends StatefulWidget {
  final String name;

  // このクラスは芪から䞎えられた匕数を保持する
  // - 匕数はstateのbuildメ゜ッドから`widget.***`ずしおアクセス可胜
  // - 匕数は垞にfinalずしお扱われる
  const Counter({this.name = 'my counter', Key? key}) : super(key: key);

  
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _increment() {
    setState(() {
      // setStateの䞭で倀を倉曎するこずで画面が再描写される。
      _counter++;
    });
  }

  
  build(context) {
    return Column(
      children: <Widget>[
        RaisedButton(
          onPressed: _increment,
          child: Text('Increment'),
        ),

        // `StatefulWidget`のメンバ倉数ずstateを組み合わせお䜿甚できる
        Text(widget.name + _counter.toString()),
      ],
    );
  }
};

# widget の倉曎怜知

StatefulWidget の匕数を State で利甚しおいる堎合widget.***を䜿っおいる堎合、それらの倉曎は自動的に反映される。もし手動で倉曎を怜知しおなにかしたいずきはdidUpdateWidgetをオヌバヌラむドする。

class _CounterState extends State<Counter> {
  
  void didUpdateWidget(Counter oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 凊理をここに曞く
  }
}

# ラむフサむクルメ゜ッド

StatefulWidget にはラむフサむクルメ゜ッドがある。initStateやdisposeをオヌバヌラむドしお蚘述する。

class _CounterState extends State<Counter> {
  // マりント時に1床だけ行いたい凊理
  
  void initState() {
    super.initState();
    // 凊理をここに曞く
  }

  // アンマりント時に行いたい凊理
  
  void dispose() {
    // 凊理をここに曞く
    super.dispose();
  }
}

# Local key

  • Loacl key を䜿うこずで、䟋えば無限リスト等においお、Widget の再利甚が効率的に行われる様になる。
    • ValueKey --- 数倀などの倀で区別する
    • ObjectKey --- オブゞェクトの id で区別する
    • UniqueKey --- 絶察に重耇しない。これを指定したコンポヌネントは必ず毎回䜜り盎される。
MyTextField(key: ValueKey(2))
MyTextField(key: ValueKey("asdf"))
MyTextField(key: ObjectKey(someObject))
MyTextField(key: Uniquekey())
  • Local key を指定しない堎合、widget の再利甚はコンポヌネントの䜍眮ず皮類に基づいお行われる。
  • 䟋えば䞋蚘のコヌドで、showFirst が true から false になった時には以䞋の凊理が行われるが、これは非効率である。本来は 1 を削陀するだけでよいため。
    • 2 の芁玠が削陀される
    • 1 の芁玠の匕数を'world'に倉曎する
if (showFirst) MyWidget('hello'), // 1
MyWidget('world'), // 2

# Global key

  • State クラスの state やメ゜ッドに察し、倖郚からアクセスしたいずきに䜿うそんなこずしおええんか
  • 圓然ながら Stateful widget に察しおのみ䜿甚できる。たた、察象の State クラス、State、メンバ倉数などが Pubric である必芁がある。
  • この動画 (opens new window)を芋るずわかりやすい。
class HomeScreen extends StatelessWidget {
  // 1. グロヌバルキヌを甚意する
  final counterGlobalKey = GlobalKey<CounterState>();

  void someParentFunction() {
    // 3. 芪偎から子のStateやメ゜ッドにアクセスできるようになる
    print(counterGlobalKey.currentState?.count);
    counterGlobalKey.currentState?.increment();
  }

  
  build(BuildContext context) {
    // 2. グロヌバルキヌをStatefulWidgetに䞎える
    return Counter(key: counterGlobalKey);
  }
}

class Counter extends StatefulWidget {
  const Counter({Key? key}) : super(key: key);

  
  createState() => CounterState();
}

// Must be public
class CounterState extends State<Counter> {
  // Must be public
  int count = 0;

  // Must be public
  void increment() {
    setState(() {
      count += 1;
    });
  }

  /* 以䞋省略 */
}

# レむアりト

  • Flutter においおはほが党おのものがりィゞェットである。
    • レむアりトのためのりィゞェット --- RowやCenterなど
    • UI ゚レメントを䜜るためのりィゞェット目に芋えるりィゞェット --- TextやRaisedButtonなど

# 1 ぀のりィゞェットを配眮する手順

たず、レむアりトりィゞェット (opens new window)を遞ぶ

Center(child:null)

次に、目に芋えるりィゞェットを䜜成する。䟋えばTextやIconなど。

Text('hello')

目に芋えるりィゞェットをレむアりトりィゞェットに远加する。レむアりトりィゞェットのchild又はchildrenに蚘茉するこずで行う。

Center(child:Text('hello'))

りィゞェットをペヌゞに配眮する。

# 耇数のりィゞェットを配眮する手順

RowやColumnを䜿っお耇数の widget を瞊又は暪方向に䞊べお配眮できる。

Row(
  // `justify-content`盞圓
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  // `align-items`盞圓
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    /* ...䞊べたいwidgets */
  ]
)
  • 䞋蚘のような抜象床の高いりィゞェットも甚意されおいるので適宜掻甚するこず。
    • Row の代わりに䜿えるListTile (opens new window)
      • 行頭末にアむコンを远加したり、3 行たでのテキストを衚瀺するこずが簡単に行える
    • Column の代わりに䜿えるListView (opens new window)
      • カラムレむアりトを簡単に䜜成できる。コンテンツが瞊にあふれる堎合は自動でスクロヌルが衚瀺される。

# Row や Column をはみ出す堎合

  • 画像等が倧きすぎお画面内に芁玠が収たらない堎合、黄色ず黒色のボヌダヌが譊告ずしお画面䞊に衚瀺される。
  • 画面内に収めたい堎合はExpandedでRow等の各子芁玠を囲む。
  • flexを指定するこずで拡倧率を蚭定できる。
Row(
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Expanded(
      child: Image.asset('images/pic1.jpg'),
    ),
    Expanded(
      flex: 2,
      child: Image.asset('images/pic2.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic3.jpg'),
    ),
  ],
);

# 詰めお衚瀺する

デフォルトではRowやColumnは main axis の方向に最倧限拡倧する。拡倧せずに詰めお衚瀺したい堎合はmainAxisSizeを蚭定する。

Row(
  mainAxisSize: MainAxisSize.min,
  children: [
    Icon(Icons.star),
    Icon(Icons.star),
    Icon(Icons.star),
  ],
);

# 読みやすいコヌドにする

  • Flutter のコヌドは、ネストが深くなるずすぐに読みづらくなる。
  • UI のたずたりごずに倉数や関数 に切り出すこずを心がけるこず。

# 画像

ネットワヌクから

Image.network('http://lorempixel.com/400/200/');

ロヌカルから

# pubspec.yaml
flutter:
  assets:
    - images/pic1.jpg
    - images/pic2.jpg
    - images/pic3.jpg
Image.asset('images/pic1.jpg');

# よく䜿うレむアりト甚のりィゞェット

  • たずはこれらの基本的なりィゞェットを䜿っお画面を䜜っおみよう。
  • りィゞェットカタログ (opens new window)
  • Standard widgets ず Material widgets がある。埌者は MaterialApp 内でしか䜿えない。

# Container

  • パディング、マヌゞン、ボヌダヌを䜿う時
  • 背景色や背景画像を倉えたい時
  • 単䞀の子を持぀

# GridView

  • りィゞェットを栌子状に配眮したい時
  • はみ出した郚分は自動でスクロヌル可胜になる
  • GridView.count 指定した数で瞊暪を分割する
  • GridView.extent アむテムの最倧幅を指定しお分割する

# ListView

  • リスト圢匏で項目を䞊べるずきに䜿う
  • 瞊方向、暪方向のどちらでも䜿甚できる
  • はみ出す堎合は自動的にスクロヌル可胜になる
  • Columnより蚭定項目は少ないものの、䜿いやすく、スクロヌルも自動的に蚭定される。

# Stack

  • りィゞェット倚くの堎合は画像に重ねおりィゞェットを配眮するずきに䜿う
  • 1 ぀目に指定したりィゞェットがベヌスりィゞェットずなる。2 ぀目以降に指定したりィゞェットがその䞊に重ねお衚瀺される。
  • スクロヌルは䞍可
  • はみだした郚分を衚瀺するかどうかは遞択できる

# Card (Material widget)

  • マテリアルデザむンのカヌドを䜿いたいずき
  • 関連する情報をたずたりにしたいずき
  • 䞞角ず圱が぀く
  • スクロヌルは䞍可
  • 初期サむズは 0x0 なので泚意。SizedBoxを䜿うずサむズを指定できる。
  • ListTile ず組みあわせお䜿うこずが倚い

# ListTile (Material widget)

  • マテリアルデザむンのリストタむル (opens new window)を䜿いたいずき
  • 3 行たでのテキストず、行頭 or 行末の任意のアむコンから構成される
  • Rowより蚭定項目は少ないが、簡単に䜿うこずができる

# 番倖線

  • Widget はあくたで蚭蚈図・蚭定・雛圢である。実際には Widget を基にしお Element ずいう実䜓が䜜成され、それが画面䞊に配眮される。
  • Element tree が web で蚀うずころの DOM tree にあたり、Widget tree が仮想 DOM にあたるむメヌゞかな
  • InheritedWidget を䜿うず、React の Context のようなこずが実珟できる。詳しくはこちら (opens new window)を読むずわかりやすい。

# Creating adaptive & responsive apps

TODO: 䞀旊パス

# Building adaptive apps

TODO: 䞀旊パス

# レむアりトの芁件(Constraints)を理解する

  • Constraint ずは以䞋の぀のこず。
    • min width
    • max width
    • min height
    • max height

# レむアりトの 3 原則

  • Flutter のレむアりトは**Constraints go down. Sizes go up. Parent sets position.**の぀のルヌルで動䜜する。web のレむアりトずは党く異なるルヌルなので芁泚意。具䜓的な動䜜は以䞋の通り。
    • りィゞェットは、parentからconstraintを受け取る。
    • りィゞェットは、childrenにconstraintを䌝えたうえで、children がどういうsizeになりたいか聞く。
    • りィゞェットは、childrenを䞀぀䞀぀配眮(position)しおいく。
    • 最埌に、りィゞェット自身がどういうsizeになりたいか芪に䌝える。

# レむアりトの制玄事項

  • りィゞェットは垌望するサむズになれない堎合もある。なぜなら芪の決めた constraint が優先されるから。
  • りィゞェットは position を自分で決められないし、知るこずもできない。なぜなら、それを決めるのは芪だから。
  • りィゞェットの size や position を正確に蚭定するためには、ツリヌ党䜓を把握する必芁がある。なぜなら、芪の size や position もそのたた芪に䟝存するから。
  • alignment は具䜓的に蚭定する必芁がある。なぜなら、もし child が parent ずは違うサむズになりたいのに、parent が十分な情報を持っおいない堎合、child のサむズは無芖される事があるから。

# レむアりトの挙動を䟋で孊ぶ

このペヌゞ (opens new window)の Examples を眺めおいくずだいたい分かる。以䞋、ハッずしたこずリスト

  • サむズ決定ロゞックは意倖ず耇雑な堎合があるので泚意。䟋えば Container なら、子芁玠があれば最小サむズ、なければ最倧サむズになるなど。
  • ConstrainedBoxは子に constraint を消極的に加える。蚀い換えるず、もずもず芪から䞎えられおいる constraint がある堎合はそちらが優先されるので泚意。
  • UnconstrainedBoxは子にいかなる制玄も加えないため、子を自然なサむズで描写できる
  • OverflowBoxは、動䜜はUnconstrainedBoxず同じだが、黄色いれブラの譊告を衚瀺しない点が異なる。
  • bounded ずいう蚀葉の意味は、width ず height に倀を持っおいるずいうこず。 逆は unbounded で、width ず height がdouble.infinityであるずいうこず。
  • Rowは子にいかなる constraint も加えないため、自然のサむズで衚瀺される。ただし、Expandedで子をラップした堎合、子のサむズは無芖される。

# Tight vs loose constraints

  • Tight constraint --- FlexFit.tight
    • (min|max)Width に同じ倀を指定し、か぀ (min|max)Height にも同じ倀を指定するこず
    • 䟋root screen の constraint: 子芁玠に画面サむズず同じになるように匷制する。この堎合、子芁玠で width を指定しおも無芖される。
  • Loose constraint --- FlexFit.loose
    • max(Width|Height)のみ指定し、min(Width|Height)は 0 であるこず
    • 䟋Center の constraint: 子芁玠は小さくなっおいい。ただし画面サむズは超えないように。

Expanded ず Flexible の違い

  • Flexible --- デフォルトではfit:FlexFit.looseである。子に瞮小を蚱可する。
  • Expanded --- Flexibleをfit:FlexFit.tightしたものず等䟡。子のサむズを無芖する。

Centerなどのいく぀かの Widget は、constraints を緩める(loosen)する効果を持぀。

# Box constraints

Flutter の内郚ではRenderBoxずいうオブゞェクトを甚いお widget を画面に配眮しおいる。

    1. なるべく倧きくなろうずする Box 䟋Center, ListView
    1. children ず同じサむズになろうずする Box 䟋Transform, Opacity
    1. 特定のサむズになろうずする Box 䟋Image, Text
  • 特殊な Box 䟋Container --- デフォルトで 1、子があれば 2、width を指定すれば 3 になる 䟋Row|Column --- 芪から受け取った Constraints によっお倉わる埌述

unbounded に関する泚意

  • unbounded(最倧幅や高さが無限倧) な constraint ず䞊蚘の 1 を組み合わせるず゚ラヌになる。
    • Row|Columnずスクロヌル可胜なりィゞェットListView|ScrollViewを倉な方向で組み合わせたずきに起こりがちなので泚意する。
  • Flex box(Row|Column)は、bounded か unbounded かにより挙動が倉わる。
    • bounded なら、そのサむズたで最倧限拡匵する
    • unbounded なら、子のサむズたで瞮小する。このずき、flexプロパティやExpandedコンポヌネントは圓然ながら䜿えない。

# State をどこで管理するか

State の管理には 3 ぀のアプロヌチがある。

    1. りィゞェット自身で管理する
    • アニメヌションの状態など、芪が知りたくもない情報はりィゞェット自身で管理するずよい。
    1. 芪が管理する
    • チェックボックスのチェック状態やむンプット欄のテキストなど、ナヌザの入力デヌタは芪が管理するず良い。
    1. 䞊蚘の 1 ずを適宜組み合わせる
    • アニメヌションの状態はりィゞェット自身で管理し、ナヌザの入力デヌタは芪が管理するなど。

# アセットず画像

䞋蚘の項目が蚘茉されおいる。必芁になったら読む。

  • 画像の読み蟌み方法
  • 画像の Variant (Dark or Light)を扱う方法
  • 解像床の異なる画像を読み蟌む方法
  • json などのテキストファむルを読み蟌む方法
  • Navigator
    • Route オブゞェクトをスタックずしお管理する widget
  • Route
    • 各画面を衚珟するオブゞェクト
    • MaterialPageRouteクラス等によっお実装されるこずが倚い
    • Named routes ず Anonymous routes がある

# Anonymous routes

任意のタむミングで䞀時的に画面を䜜成しお衚瀺したいずき

// 遷移したいずき
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) {
    return OtherScreen();
  }),
);

// 戻るずきは
Navigator.pop(context);

# Named routes

// ルヌティング蚭定
MaterialApp(
  routes: {
    '/': (context) => HomeScreen(),
    '/details': (context) => DetailScreen(),
  },
);

// 遷移したいずき
Navigator.pushNamed(
  context,
  '/details',
);

// 戻りたいずき
Navigator.pop(context);

# onGenerateRoute

  • 最も柔軟な route の䜜成方法。
  • Url をパヌスしお倀を取埗し、画面に枡すこずも可胜
MaterialApp(
  onGenerateRoute: (settings) {
    // Handle '/'
    if (settings.name == '/') {
      return MaterialPageRoute(builder: (context) => HomeScreen());
    }

    // Handle '/details/:id'
    var uri = Uri.parse(settings.name);
    if (uri.pathSegments.length == 2 &&
        uri.pathSegments.first == 'details') {
      var id = uri.pathSegments[1];
      return MaterialPageRoute(builder: (context) => DetailScreen(id: id));
    }

    // その他
    return MaterialPageRoute(builder: (context) => UnknownScreen());
  },
);
  • Navigator 2.0 はあたりにも耇雑なので、そのたた䜿うのは危険。なお、Flutter 開発チヌムもこういった評䟡を認識しおおり、より簡易なパッケヌゞの開発が始たっおいる。ずはいえ、web の堎合は URL ずの同期が必芁ずなるこずが倚いので、v2.0 がほが必須ずなる。
  • Navigator 2.0 が必芁な堎合はサヌドパヌティのラッパヌラむブラリを䜿うずよい。2021/08 珟圚ではroutemaster (opens new window)が䜿い良さそう。

参考資料

  • https://zenn.dev/ntaoo/articles/6641e846765da1
  • https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade

routemaster を䜿うこずで、Navigator2.0 を劇的にシンプルに扱うこずができる。ボトムタブぞの察応なども簡単に行える。詳现は routemaster の ペヌゞ (opens new window) を参照。

final routes = RouteMap(
  routes: {
    '/': (_) => CupertinoTabPage(
          child: HomePage(),
          paths: ['/feed', '/settings'],
        ),

    '/feed': (_) => MaterialPage(child: FeedPage()),
    '/settings': (_) => MaterialPage(child: SettingsPage()),
    '/feed/profile/:id': (info) => MaterialPage(
      child: ProfilePage(id: info.pathParameters['id'])
    ),
  }
);

void main() {
  runApp(
      // ここは定型文
      MaterialApp.router(
        routerDelegate: RoutemasterDelegate(routesBuilder: (context) => routes),
        routeInformationParser: RoutemasterParser(),
      ),
  );
}

// 遷移したいずきは以䞋のようにする
Routemaster.of(context).push('/feed/profile/1');

# Deep linking

モバむルだけに関係する話のようなので䞀旊パス

# URL 戊略

  • web の開発においお、URL の圢匏を「ハッシュあり」ず「ハッシュなし」から遞ぶこずができる。デフォルトはハッシュあり。倉曎したい堎合はこちら (opens new window)を参考に蚭定する。
  • 必芁があれば Base URL の蚭定も行える

# Animations

パス

# --- Data & backend ---

# State management

他のリアクティブ or 宣蚀的な環境(React など)の知芋があればこの章はスキップしおいいずのこずなので、䞀郚のみ抜粋。

  • 甚語
    • Ephemeral state --- アニメヌションの状態など
    • App state --- ナヌザが入力䞭の文字など

# Provider package

  • 最もシンプルな State の管理方法。React の Context に䌌おいる。
  • InheritedWidget, InheritedNotifier, InheritedModelずいう䜎レベルなものを䜿いやすくしたもの
  • provider を䜿うには以䞋の぀のこずを理解する必芁がある
    • ChangeNotifier
    • ChangeNotifierProvider
    • Consumer

# ChangeNotifier

  • notifyListeners()により、子孫に倉曎を通知する圹割を持぀
  • 逆に蚀うず、Consumer は ChangeNotifier を賌読するこずができる
  • flutter:foundation由来
class CartModel extends ChangeNotifier {
  final List<Item> _items = [];

  get items => ListView(_items);

  int get totalPrice => _items.length * 12345;

  void add(Item item) {
    _items.add(item);
    notifyListeners(); // 通知
  }

  void removeAll() {
    _items.clear();
    notifyListeners(); // 通知
  }
}

# ChangeNotifierProvider

  • ChangeNotifierのむンスタンスを子孫に枡す圹割を持぀
  • 最も近い共通の祖先に配眮する
  • providerパッケヌゞ由来
ChangeNotifierProvider(
  create: (context) => CartModel(),
  child: const CommonAncestor(),
)

// 耇数のNotifierを䜿いたい堎合は以䞋のようにする
MultiProvider(
  providers: [
    ChangeNotifierProvider(create: (context) => CartModel()),
    Provider(create: (context) => SomeOtherClass()),
  ],
  child: const CommonAncestor(),
),

# Comsumer

  • Generic は必須。型情報を基に、どの Notifier の倀を取埗するかが決定されるため。
  • child はパフォヌマンス最適化のために䜿われる。詳现はこちら (opens new window)を参照。
Consumer<CartModel>(
  builder: (context, cart, child) {
    return Text("Total price: ${cart.totalPrice}");
  },
);
  • なお、メ゜ッドだけにアクセスできれば足りる堎合は、Consumerだずコストが高く぀くため、Provider.ofをlisten: falseにしお䜿うず良い。
Provider.of<CartModel>(context, listen: false).removeAll();

# State 管理の遞択肢

  • setState
    • 原始的。Ephemeral state の管理に最適。
  • InheritedWidget & InheritedModel
    • 祖先ず子孫の間で state をやり取りするための䜎レベルな手法。
    • provider は実質的にはこれらを䜿っおいる
  • Redux
    • 説明䞍芁
  • Fish-Redux
    • 省略。䞭から倧芏暡なアプリに最適ずのこず。
  • BLoC / Rx
    • Stream, Observable ベヌス
  • GetIt
    • Service locator ベヌス
    • BuildContextが䞍芁
  • MobX
    • Observable, reaction ベヌス
  • Flutter Commands
    • Command パタヌン
    • ValueNotifiersを䜿っおいる
    • GetIt ず組み合わせるのがおすすめ
  • Binder
    • recoid にむンスパむアされおいる
    • InheritedWidgetを䜿っおいる
  • GetX
  • Riverpod
    • provider を䜿いやすく改造したもの。
    • Flutter SDK ぞの䟝存が䞀切ない
  • states_rebuilder
    • dependency injection による状態管理 + integrated router から構成される

# Riverpod

Riverpod (opens new window)の特城

  • ゚ラヌをコンパむル時点で怜出できる
  • ネストをなくせる
  • テスト可胜である

# JSON & serialization

JSON のシリアラむズ・デシリアラむズをどうやるか

  • PoC などの小さなプロゞェクトでは manual serialization が最適
    • dart:convertを䜿う
    • 生の JSON をjsonDecode()に䞎えるずMap<String, dynamic>が埗られる。
    • 容易にランタむム゚ラヌが起こるので泚意
  • 䞭芏暡・倧芏暡なプロゞェクトでは Code generation が最適
    • json_serializableやbuild_valueなどの倖郚ラむブラリを䜿っお行う
    • コンパむル時点で゚ラヌチェックが可胜
    • モデルクラスを監芖し、自動的に゚ンコヌディング甚のコヌドを生成する

# 手動シリアラむズ (inline)

  • タむポにより容易に゚ラヌが起こりうる
  • 型情報が倱われおいる
Map<String, dynamic> user = jsonDecode(jsonString);

print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');

# 手動シリアラむズ (inside model classes)

  • モデルに゚ンコヌド・デコヌドの機胜をもたせる方法
  • 型情報は蚭定されるものの、冗長で、これ以䞊の耇雑化には耐えられない。
class User {
  final String name;
  final String email;

  User(this.name, this.email);

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        email = json['email'];

  Map<String, dynamic> toJson() => {
        'name': name,
        'email': email,
      };
}

# Code generation によるシリアラむズ

以䞋、json_serializable を䜿った䟋を蚘茉。たず、pubspec.yamlの蚭定を行う。

dependencies:
  json_annotation: <latest_version>

dev_dependencies:
  build_runner: <latest_version>
  json_serializable: <latest_version>

モデルクラスを json_serializable クラスに眮き換える。

// user.dart

import 'package:json_annotation/json_annotation.dart';

// Userクラスが自動生成されたコヌドにアクセスするためのおたじない。
// 生成されるファむル名である`<元の゜ヌスファむル名>.g.dart`を指定する。
part 'user.g.dart';

// json_serializableの凊理察象にする
()
class User {
  User(this.name, this.email);

  String name;
  String email;

  // おたじない
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  // おたじない
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

サヌバサむドずフロントサむドでキヌ名が違う堎合などは、䞋蚘のようにする。

// フィヌルドごずに蚭定する方法
(name: 'registration_date_millis')
final int registrationDateMillis;

// 䞀括で蚭定する方法(JSONのスネヌクケヌス<=>モデルのキャメルケヌス)
 (fieldRename: FieldRename.snake)

その他、必須芁件なども蚭定できる

(defaultValue: false) // なければこの倀をセットする
(required: true) // なければ゚ラヌを䞊げる
(ignore: true) // コヌド自動生成の凊理察象から倖す

コヌドの自動生成方法

  • 䞀回のみ flutter pub run build_runner build
  • 継続しおりォッチ flutter pub run build_runner watch

実際の利甚方法は以䞋の通り。これで、もはやシリアラむズに関する責務はラむブラリに委譲された。

// デコヌディング
Map<String, dynamic> userMap = jsonDecode(jsonString);
var user = User.fromJson(userMap);

// ゚ンコヌディング
var userMap = User.toJson(user);
String json = jsonEncode(userMap);

なお、モデルをネストさせるこずも可胜。ただし、ネストしたモデルは JSON にしたずきにデフォルトでは展開されないので、explicitToJsonの指定が必芁。詳现はこちら (opens new window)。

(explicitToJson: true)

# HTTP 通信の方法

䞋蚘を䜿う。 https://github.com/dart-lang/http