Flutterに「Get」というなんかすごそうな状態管理&ルーティング管理パッケージを見つけたので軽く紹介する

![get.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176871/799b9611-1fac-683d-46a9-68702f273e23.png)

# 「Get」パッケージについて

https://github.com/jonataslaw/get
https://pub.dev/packages/get

先日、redditで以下ようなsubmissionが投稿されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176871/6899dc29-57fa-5b09-9d99-330e09ae43d7.png)

主に、状態管理やルーティング管理などを提供しているみたいです。
pub.devやレポジトリのページを見てもらえばわかるように、ReadMeがくそ長いです。僕にはそんな大量の英語読めません。
多機能すぎて意見が分かれてるっぽいですが、Flutterはビルド時に無駄なものは削除してくれるので個人的にはあまり気にならないです。

英語とか全然読めないので重要そうなとこだけ抜粋します。

## ルーティング管理

[redditのディスカッション](https://www.reddit.com/r/FlutterDev/comments/h8nivn/get_a_package_that_every_flutter_developer_should/)を見る限り、これが一番評価されてそうな感じです。

>Get will save hours of development, and will extract the maximum performance that your application can deliver, being easy for beginners, and accurate for experts. Navigate without context, open dialogs, snackbars or bottomsheets from anywhere in your code, Manage states and inject dependencies in an easy and practical way. Get is secure, stable, up-to-date, and offers a huge range of APIs that are not present on default framework.

Googleほんやく

>Getは開発時間を節約し、アプリケーションが提供できる最大のパフォーマンスを引き出します。初心者にとっては簡単で、エキスパートにとっては正確です。 コンテキストなしでナビゲートし、コードのどこからでもダイアログ、スナックバー、またはボトムシートを開いて、状態を管理し、依存関係を簡単かつ実用的な方法で注入します。 Getは安全で安定しており、最新であり、デフォルトのフレームワークには存在しない幅広いAPIを提供します。

コンテキストなしでナビゲートしたり、Snackbarを呼び出せるようです。

```Dart:コンテキストなしでSnackbar
Get.snackbar('Hi', 'i am a modern snackbar');
```

```Dart:カスタマイズも可能
Get.snackbar(
  "Hey i'm a Get SnackBar!", // title
  "It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!", // message
  icon: Icon(Icons.alarm), 
  shouldIconPulse: true,
  onTap:(){},
  barBlur: 20,
  isDismissible: true,
  duration: Duration(seconds: 3),
);
```

パッケージの内部ソースコードをみればすごい実装がなされているみたいですが、自分はそこまでは見ません。

### 名前なしでのルーティング

新しい画面への遷移。push

```Dart
Get.to(NextScreen());
```

Navigator.pop(context)的なもの。snackbars、dialogs、bottomsheetもこれで閉じる。

```Dart
Get.back();
```

画面遷移し、１つ前の画面には戻れない。

```Dart
Get.off(NextScreen());
```

以前の画面すべてをキャンセルし、画面遷移。

```Dart
Get.offAll(NextScreen());
```


画面遷移し、元の画面に戻ってきたときに値を受け取るには、↓で遷移した後、

```Dart
var data = await Get.to(Payment());
```

遷移先で↓を実行すると遷移元のdataに"success"が入る。

```Dart
Get.back(result: 'success');
```

また、以下のようにnavigatorを使うと、コンテキストなしで標準のルーティングが使える。

```Dart
// Default Flutter navigator
Navigator.of(context).push(
  context,
  MaterialPageRoute(
    builder: (BuildContext context) { 
      return HomePage();
    },
  ),
);

// Get using Flutter syntax without needing context
navigator.push(
  MaterialPageRoute(
    builder: (_) { 
      return HomePage();
    },
  ),
);

// Get syntax (It is much better, but you have the right to disagree)
Get.to(HomePage());

```

### 名前ありでのルーティング

まずは名前の定義

```Dart
void main() {
  runApp(
    GetMaterialApp(
      initialRoute: '/',
      namedRoutes: {
        '/': GetRoute(page: MyHomePage()),
        '/second': GetRoute(page: Second()),
        '/third': GetRoute(page: Third(),transition: Transition.cupertino);
      },
    )
  );
}
```

あとはこんな感じで使える。

```Dart
Get.toNamed("/NextScreen");
Get.offNamed("/NextScreen");
Get.offAllNamed("/NextScreen");
```

値を送る。

```Dart
Get.toNamed("/NextScreen", arguments: 'Get is the best');
```

値を受け取る。

```Dart
print(Get.arguments);
```

WebのGET通信のような感じで送ることもできます。

```Dart
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
```

受け取る。

```Dart
print(Get.parameters['id']);
// out: 354
print(Get.parameters['name']);
// out: Enzo
```

さらに、↓のように定義することで

```Dart
void main() {
  runApp(
    GetMaterialApp(
      initialRoute: '/',
      namedRoutes: {
        '/': GetRoute(page: MyHomePage()),
        // :userはルートではなくパラメータ名
        '/second/:user': GetRoute(page: Second()), // receive ID
        '/third': GetRoute(page: Third(),transition: Transition.cupertino);
      },
    )
  );
}
```

↓のようにして送ることができます。

```Dart
Get.toNamed("/second/34954");
```

受け取る。

```Dart
print(Get.parameters['user']);
// out: 34954
```

これは、Web版を開発するときに使えそうですね。

なにより、**contextが要らない**のでControllerクラスなどからルーティングが可能です。

めちゃくちゃいい感じだとおもいませんか？
つぎは状態管理についてです。

## 状態管理

Getには、**Simple State Manager**と**Reactive State Manager**という2種類のものがあります。

### Simple State Manager

>Get has a state manager that is extremely light and easy, which does not use ChangeNotifier, will meet the need especially for those new to Flutter, and will not cause problems for large applications.

名前的にもシンプルなステートの管理に向いてそうですね。

#### メリット
>1. Update only the required widgets.

>2. Does not use changeNotifier, it is the state manager that uses less memory (close to 0mb).

>3. Forget StatefulWidget! With Get you will never need it. With the other state managers, you will probably have to use a StatefulWidget to get the instance of your Provider, BLoC, MobX Controller, etc. But have you ever stopped to think that your appBar, your scaffold, and most of the widgets that are in your class are stateless? So why save the state of an entire class, if you can only save the state of the Widget that is stateful? Get solves that, too. Create a Stateless class, make everything stateless. If you need to update a single component, wrap it with GetBuilder, and its state will be maintained.

>4. Organize your project for real! Controllers must not be in your UI, place your TextEditController, or any controller you use within your Controller class.

>5. Do you need to trigger an event to update a widget as soon as it is rendered? GetBuilder has the property "initState", just like StatefulWidget, and you can call events from your controller, directly from it, no more events being placed in your initState.

>6. Do you need to trigger an action like closing streams, timers and etc? GetBuilder also has the dispose property, where you can call events as soon as that widget is destroyed.

>7. Use streams only if necessary. You can use your StreamControllers inside your controller normally, and use StreamBuilder also normally, but remember, a stream reasonably consumes memory, reactive programming is beautiful, but you shouldn't abuse it. 30 streams open simultaneously can be worse than changeNotifier (and changeNotifier is very bad).

>8. Update widgets without spending ram for that. Get stores only the GetBuilder creator ID, and updates that GetBuilder when necessary. The memory consumption of the get ID storage in memory is very low even for thousands of GetBuilders. When you create a new GetBuilder, you are actually sharing the state of GetBuilder that has a creator ID. A new state is not created for each GetBuilder, which saves A LOT OF ram for large applications. Basically your application will be entirely Stateless, and the few Widgets that will be Stateful (within GetBuilder) will have a single state, and therefore updating one will update them all. The state is just one.

>9. Get is omniscient and in most cases it knows exactly the time to take a controller out of memory. You should not worry about when to dispose of a controller, Get knows the best time to do this.

要約すると、必要なウィジェットのみを更新し、使用するメモリが少なく、簡単に使用でき、StatefulWidgetが要らなくなるということだと思います。たぶん...
以下のように使うことができます。

```Dart:Usage
// Create controller class and extends GetController
class Controller extends GetController {
  int counter = 0;
  void increment() {
    counter++;
    update(); // use update() to update counter variable on UI when increment be called
  }
}
// On your Stateless/Stateful class, use GetBuilder to update Text when increment be called 
GetBuilder<Controller>(
  init: Controller(), // INIT IT ONLY THE FIRST TIME
  builder: (_) => Text(
    '${_.counter}',
  ),
)
//Initialize your controller only the first time. The second time you are using ReBuilder for the same controller, do not use it again. Your controller will be automatically removed from memory as soon as the widget that marked it as 'init' is deployed. You don't have to worry about that, Get will do it automatically, just make sure you don't start the same controller twice.
```

### Reactive State Manager

>remembering that the more "individual" widgets you have, the more the performance of GetX will stand out, while the performance of GetBuilder should be superior, when there is multiple change of state.

「個々の」ウィジェットの数が多いほど、GetXのパフォーマンスのほうが優れているらしいです。

**Simple State Manager**では、**GetBuilder**を使っていたのに対し、**Reactive State Manager**では**GetX**を使用します。

```Dart:Usage
final count1 = 0.obs;
final count2 = 0.obs;
int get sum => count1.value + count2.value;
```
```Dart
GetX<Controller>(
  builder: (_) {
    print("count 1 rebuild");
    return Text('${_.count1.value}');
  },
),
GetX<Controller>(
  builder: (_) {
    print("count 2 rebuild");
    return Text('${_.count2.value}');
  },
),
GetX<Controller>(
  builder: (_) {
    print("count 3 rebuild");
    return Text('${_.sum}');
  },
),
```

- カウント1がインクリメントされると、カウント1とカウント2のみが再構築される。
- カウント2を変更すると、カウント2とカウント3のみが再構築される。
- すでに1が格納されているカウント1に再度1を追加しても再構築はされない。
- すでに1が格納されているカウント1に再度1を追加し、カウント2を変更すると、カウント2とカウント3のみが再構築される。

## カウンターアプリの例

例の、Flutterで新規プロジェクトを作ったときに作られるカウンターアプリは、コメント付きで100行以上あります。
しかし、**Get**を使うことによって、カウンターアプリにさらに画面間でのデータ共有とコメントをつけて26行で済みます。

ステップ１：MaterialAppをGetMaterialAppに変更

```Dart
void main() => runApp(GetMaterialApp(home: Home()));
```
ステップ２：ビジネスロジッククラスを作成し、その中に変数とメソッド、コントローラーを配置する。
「.obs」を使用することで変数を監視可能にすることができる。

```Dart
class Controller extends RxController{
  var count = 0.obs; 
  increment() => count.value++;
}
```

ステップ３：ビューを作成する。

```Dart
class Home extends StatelessWidget {

  // Get.put（）を使用してクラスをインスタンス化し、すべての「子」ルートで使用可能にする。
  final Controller c = Get.put(Controller());

  @override
  Widget build(context) => Scaffold(
      // countが変更されるたびに、Obx（（）=>を使用してText（）を更新する。
      appBar: AppBar(title: Obx(() => Text("Clicks: " + c.count.string))),

      // 8行のNavigator.pushを単純なGet.to（）に置き換える。 コンテキストは不要。
      body: Center(child: RaisedButton(
              child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
      floatingActionButton:
          FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
}

class Other extends StatelessWidget {
  // Getに、別のページで使用されているコントローラーを見つけてリダイレクトするように要求する。
  final Controller c = Get.find();

  @override
  Widget build(context){
     // 更新されたcountにアクセスして表示
     return Scaffold(body: Center(child: Text(c.count.string)));
}
```


# まとめ
どうでしたか？
僕は英語が全く読めないので、ほんの一部だけ紹介しました。

ルーティングに関しては**Get**を使うのがいいんじゃないかな？と思います。
状態管理に関してはそれぞれ一長一短あり、僕には何とも言えないです。
**Get**は**freezed**+**Provider**パッケージのような、コード生成を必要とせず、簡単に書けるため、**Provider**を使うのをやめて**Get**を使っている人もいるみたいです。
詳しいところは僕にはわからないのでFlutterの先人様たちにパスします...

なにかわかりましたらコメントよろしくお願いします。