「Get」パッケージについて
https://github.com/jonataslaw/get
https://pub.dev/packages/get
先日、redditで以下ようなsubmissionが投稿されました。
主に、状態管理やルーティング管理などを提供しているみたいです。
pub.devやレポジトリのページを見てもらえばわかるように、ReadMeがくそ長いです。僕にはそんな大量の英語読めません。
多機能すぎて意見が分かれてるっぽいですが、Flutterはビルド時に無駄なものは削除してくれるので個人的にはあまり気にならないです。
英語とか全然読めないので重要そうなとこだけ抜粋します。
ルーティング管理
redditのディスカッションを見る限り、これが一番評価されてそうな感じです。
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を呼び出せるようです。
Get.snackbar('Hi', 'i am a modern snackbar');
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
Get.to(NextScreen());
Navigator.pop(context)的なもの。snackbars、dialogs、bottomsheetもこれで閉じる。
Get.back();
画面遷移し、1つ前の画面には戻れない。
Get.off(NextScreen());
以前の画面すべてをキャンセルし、画面遷移。
Get.offAll(NextScreen());
画面遷移し、元の画面に戻ってきたときに値を受け取るには、↓で遷移した後、
var data = await Get.to(Payment());
遷移先で↓を実行すると遷移元のdataに"success"が入る。
Get.back(result: 'success');
また、以下のようにnavigatorを使うと、コンテキストなしで標準のルーティングが使える。
// 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());
名前ありでのルーティング
まずは名前の定義
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
namedRoutes: {
'/': GetRoute(page: MyHomePage()),
'/second': GetRoute(page: Second()),
'/third': GetRoute(page: Third(),transition: Transition.cupertino);
},
)
);
}
あとはこんな感じで使える。
Get.toNamed("/NextScreen");
Get.offNamed("/NextScreen");
Get.offAllNamed("/NextScreen");
値を送る。
Get.toNamed("/NextScreen", arguments: 'Get is the best');
値を受け取る。
print(Get.arguments);
WebのGET通信のような感じで送ることもできます。
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
受け取る。
print(Get.parameters['id']);
// out: 354
print(Get.parameters['name']);
// out: Enzo
さらに、↓のように定義することで
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
namedRoutes: {
'/': GetRoute(page: MyHomePage()),
// :userはルートではなくパラメータ名
'/second/:user': GetRoute(page: Second()), // receive ID
'/third': GetRoute(page: Third(),transition: Transition.cupertino);
},
)
);
}
↓のようにして送ることができます。
Get.toNamed("/second/34954");
受け取る。
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.
名前的にもシンプルなステートの管理に向いてそうですね。
メリット
- Update only the required widgets.
- Does not use changeNotifier, it is the state manager that uses less memory (close to 0mb).
- 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.
- Organize your project for real! Controllers must not be in your UI, place your TextEditController, or any controller you use within your Controller class.
- 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.
- 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.
- 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).
- 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.
- 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が要らなくなるということだと思います。たぶん...
以下のように使うことができます。
// 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を使用します。
final count1 = 0.obs;
final count2 = 0.obs;
int get sum => count1.value + count2.value;
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行で済みます。
ステップ1:MaterialAppをGetMaterialAppに変更
void main() => runApp(GetMaterialApp(home: Home()));
ステップ2:ビジネスロジッククラスを作成し、その中に変数とメソッド、コントローラーを配置する。
「.obs」を使用することで変数を監視可能にすることができる。
class Controller extends RxController{
var count = 0.obs;
increment() => count.value++;
}
ステップ3:ビューを作成する。
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の先人様たちにパスします...
なにかわかりましたらコメントよろしくお願いします。