Flutter で流行りの Riverpod を使ってみた

# Riverpod とは

FLutter 界隈で最近流行りの状態管理ライブラリ。
Provider よりいい感じに状態管理が行える。
~~詳しいことはあんまりわかってない~~

# 今回作るもの

Flutter でアプリケーションを作ると出てくることでおなじみの、カウントアップアプリ。
右下の「＋」をクリック or タップすると、中央の数字が増えていく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252342/91f6c3ad-7eea-2ba4-e53e-d283fcdc3a3c.png)

最終形はここに。
https://github.com/tktcorporation/Flutter-StateManagement/tree/v1

# 構成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252342/a5acd83e-e370-be47-7545-7d5c7f21ee4a.png)

- Domain は状態管理だったりのことを考えない純粋なオブジェクト
- Controller は Domain に依存し、ChangeNotifier を継承することで、状態管理を担当する

App の特定のスコープ内で Riverpod の ChangeNotifierProvider に Controller を渡し、それを用いて状態を管理する。
というような方法をとっている。

~~有用な構成なのかはわかっていない~~

# 実装

## Domain

状態管理だったりのことを考えない純粋なオブジェクト

```dart
class Counter {
  int _value = 0;

  int get value => _value;

  void increment() {
    _value++;
  }
}
```

## Controller

Domain に依存し、ChangeNotifier を継承することで、状態管理を担当する

```dart
import 'package:flutter/material.dart';
import 'package:stateManagement/domain/counter.dart';

class CounterController extends ChangeNotifier {
  CounterController(Counter counter) : this._counter = counter;
  Counter _counter;

  void increment() {
    this._counter.increment();
    notifyListeners();
  }

  int getCount() {
    return this._counter.value;
  }
}
```

## Component

### CountText

カウントを表示する

```dart
class CountText extends StatelessWidget {
  const CountText(this._count);
  final int _count;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(
            'You have pushed the button this many times:',
          ),
          Text(
            _count.toString(),
            style: Theme.of(context).textTheme.headline4,
          ),
        ],
      ),
    );
  }
}
```

### IncrementButton

押すとカウントが増えるボタン

```dart
typedef void Increment();

class IncrementButton extends StatelessWidget {
  const IncrementButton(this._increment);

  final Increment _increment;

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      onPressed: this._increment,
      tooltip: 'Increment',
      child: Icon(Icons.add),
    );
  }
}
```

## Page

```dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// import counter.dart
// import counter_controller.dart
// import count_text.dart
// import increment_button.dart

class CounterPage extends StatelessWidget {
  CounterPage(this._title);
  final String _title;
  final counterProvider = ChangeNotifierProvider<CounterController>(
      (_) => CounterController(Counter()));

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(this._title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Consumer(
                builder: (_, watch, __) =>
                    CountText(watch(counterProvider).getCount()),
              )
            ],
          ),
        ),
        floatingActionButton: IncrementButton(context.read(counterProvider).increment));
  }
}
```

## App

```dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// import counter_page.dart;

class RiverpodTestApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return ProviderScope(
      child: MaterialApp(
        title: 'CounterApp',
        theme: ThemeData(
          // This is the theme of your application.
          primarySwatch: Colors.orange,
          // This makes the visual density adapt to the platform that you run
          // the app on. For desktop platforms, the controls will be smaller and
          // closer together (more dense) than on mobile platforms.
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: CounterPage('Counter'),
      ),
    );
  }
}
```

## Main

```dart
import 'package:flutter/material.dart';
// import riverpod_test.dart

void main() {
  runApp(RiverpodTestApp());
}
```

ここまで書いて起動してあげれば、カウントアプリは正常に動作するはず。

# まとめ

自分が行っているような形での状態管理を行っている記事は見当たらなかったので、一つの形として残してみる。
実践運用した構成ではないので、後日、実際に使ってみた感想を追記する予定。

「そのやり方はここがよろしくないよー」等あれば、コメントお願いしたいです。