🚄

【コピペOK】Riverpod向け VSCodeコードスニペット集

2022/03/22に公開

こんな方におすすめの記事です。

  • FlutterのRiverpodの構文が長くて大変...
  • VSCode使ってる
  • MVVM+Repository設計パターンをよく使う

Flutterの状態管理ライブラリRiverpodのコードは括弧やジェネリクス多用することになり、コード書くの大変ですよね~。

というわけで、Riverpod向けのコードスニペットを作成してみましたのでコピペしてご利用いただければと思います。

使い方簡単解説

  1. VSCode > ユーザー設定 > ユーザースニペット

  1. 登録したい言語を選択。今回はdart。

  2. dart.jsonに次章のコードスニペットを登録する

【コピペOK】Riverpod向けコードスニペット

登録したいコードスニペットは以下の通りです。
コピペすればそのまま使えます。

  • 2022/03/22 新規
  • 2022/10/25 改訂しました!Riverpodだけでなく、その他便利なコードスニペットも追加しておきました。
  • 2022/10/28 StatefulHookConsumerWidgetとHookConsumerWidget作成用のスニペットを追加しました!
dart.json
{
	// Place your snippets for dart here. Each snippet is defined under a snippet name and has a prefix, body and 
	// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
	// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the 
	// same ids are connected.
	// Example:
	// "Print to console": {
	// 	"prefix": "log",
	// 	"body": [
	// 		"console.log('$1');",
	// 		"$2"
	// 	],
	// 	"description": "Log output to console"
	// }
    "stfh": {
        "prefix": "stfh",
        "body": [
            "import 'package:flutter/material.dart';",
            "import 'package:hooks_riverpod/hooks_riverpod.dart';",
            "",
            "class $1Page extends StatefulHookConsumerWidget {",
            "  const $1Page({super.key});",
            "",
            "  @override",
            "  ConsumerState<ConsumerStatefulWidget> createState() => _$1PageState();",
            "}",
            "",
            "class _$1PageState extends ConsumerState<$1Page> {",
            "  @override",
            "  Widget build(BuildContext context) {",
            "    return Scaffold(",
            "      appBar: AppBar(",
            "        title: const Text('title'),",
            "      ),",
            "      body: _buildBody(),",
            "    );",
            "  }",
            "",
            "  Widget _buildBody() {",
            "    return Container();",
            "  }",
            "}",
        ],
        "description": ""
    },
    "stlh": {
        "prefix": "stlh",
        "body": [
            "import 'package:flutter/material.dart';",
            "import 'package:hooks_riverpod/hooks_riverpod.dart';",
            "",
            "class $1Page extends HookConsumerWidget {",
            "  const $1Page({super.key});",
            "  @override",
            "  Widget build(BuildContext context, WidgetRef ref) {",
            "    return Scaffold(",
            "      appBar: AppBar(",
            "        title: const Text('title'),",
            "      ),",
            "      body: _buildBody(ref),",
            "    );",
            "  }",
            "",
            "  Widget _buildBody(WidgetRef ref) {",
            "    return Container();",
            "  }",
            "}",
        ],
        "description": ""
    },
    "notif": {
        "prefix": "notif",
        "body": [
            "import 'package:hooks_riverpod/hooks_riverpod.dart';",
            "",
            "import '$2_state.dart';",
            "",
            "final $2StateProvider =",
            "    StateNotifierProvider.autoDispose<$1Notifier, $1State>(",
            "  (ref) => $1Notifier(ref.read($2RepositoryProvider),),",
            ");",
            "",
            "class $1Notifier extends StateNotifier<$1State> {",
            "  $1Notifier(this._repository) : super($1State());",
            "",
            "  final $1RepositoryBase _repository;",
            "}",
            "",
        ],
        "description": ""
    },
    "controllerp": {
        "prefix": "controllerp",
        "body": [
            "",
            "import 'package:hooks_riverpod/hooks_riverpod.dart';",
            "",
            "import '$2_state.dart';",
            "",
            "final $2ControllerProvider =",
            "    StateNotifierProvider.autoDispose<$1Notifier, $1State>(",
            "  (ref) => $1Controller(ref.read($1RepositoryProvider),),",
            ");",
            "",
            "class $1Controller extends StateNotifier<$1State> {",
            "  $1Controller(this._repository) : super($1State());",
            "",
            "  final $1RepositoryBase _repository;",
            "}",
        ],
        "description": ""
    },
    "repo": {
        "prefix": "repo",
        "body": [
            "",
            "import 'package:hooks_riverpod/hooks_riverpod.dart';",
            "",
            "import 'package:uuid/uuid.dart';",
            "",
            "class XXXEntity {",
            "  XXXEntity({",
            "    required this.id,",
            "    required this.value,",
            "  });",
            "  String id;",
            "  int value;",
            "}",
            "",
            "abstract class XXXRepositoryBase {",
            "  Future<XXXEntity?> fetch(String id);",
            "  Future<XXXEntity?> fetchByValue(int value);",
            "  Future<List<XXXEntity>> fetchAll();",
            "  Future<void> save(XXXEntity entity);",
            "  Future<void> remove(XXXEntity entity);",
            "}",
            "",
            "final xxxRepositoryProvider =",
            "    Provider<XXXRepositoryBase>((ref) => XXXRepository());",
            "",
            "class XXXRepository implements XXXRepositoryBase {",
            "  XXXRepository() {",
            "    final initialData = XXXEntity(",
            "      id: const Uuid().v4(),",
            "      value: 100,",
            "    );",
            "    save(initialData);",
            "  }",
            "",
            "  Map<String, XXXEntity> data = <String, XXXEntity>{};",
            "",
            "  @override",
            "  Future<XXXEntity?> fetch(String id) async {",
            "    await Future.delayed(const Duration(seconds: 2));",
            "    try {",
            "      return data.values.firstWhere((element) => element.id == id);",
            "    } on StateError catch(_) {",
            "      return null;",
            "    }",
            "  }",
            "  ",
            "  @override",
            "  Future<XXXEntity?> fetchByValue(int value) async {",
            "    await Future.delayed(const Duration(seconds: 2));",
            "    try {",
            "      return data.values.firstWhere((element) => element.value == value);",
            "    } on StateError catch(_) {",
            "      return null;",
            "    }",
            "  }",
            "",
            "  @override",
            "  Future<List<XXXEntity>> fetchAll() async {",
            "    await Future.delayed(const Duration(seconds: 2));",
            "",
            "    return data.values.toList();",
            "  }",
            "",
            "  @override",
            "  Future<void> save(XXXEntity entity) async {",
            "    await Future.delayed(const Duration(seconds: 2));",
            "    data[entity.id] = entity;",
            "  }",
            "",
            "  @override",
            "  Future<void> remove(XXXEntity entity) async {",
            "    await Future.delayed(const Duration(seconds: 2));",
            "    data.remove(entity.id);",
            "  }",
            "}",
            "",
        ],
        "description": ""
    },
    "frf": {
        "prefix": "frf",
        "body": [
            "import 'package:freezed_annotation/freezed_annotation.dart';",
            "",
            "part '$TM_FILENAME_BASE.freezed.dart';",
            "part '$TM_FILENAME_BASE.g.dart';",
            "",
            "@freezed",
            "class $1 with _\\$$1 {",
            "",
            "  factory $1({",
            "    ",
            "  }) = _$1;",
            "",
            "  factory $1.fromJson(Map<String, dynamic> json) => _\\$$1FromJson(json);",
            "}",
        ],
        "description": ""
    },
    "refw": {
        "prefix": "refw",
        "body": [
            "final $1 = ref.watch($1Provider);",
        ],
        "description": ""
    },
    "refr": {
        "prefix": "refr",
        "body": [
            "ref.read($1).$2;",
        ],
        "description": ""
    },
    "refn": {
        "prefix": "refn",
        "body": [
            "final $1 = ref.watch($1Provider.notifier);",
        ],
        "description": ""
    },
    "factory": {
        "prefix": "factory",
        "body": [
            "factory $1.create() {",
            "  return $1(",
            "",
            "  );",
            "}",
            "",
        ],
        "description": ""
    },
    "uuid": {
        "prefix": "uuid",
        "body": [
            "const Uuid().v4()",
        ],
        "description": ""
    },
    "sca": {
        "prefix": "sca",
        "body": [
            "return Scaffold(",
            "      appBar: AppBar(",
            "        title: const Text('$1'),",
            "      ),",
            "      body: Container(),",
            "    );",
        ],
        "description": ""
    },
    "size": {
        "prefix": "size",
        "description": "",
        "body": [
            "final size = MediaQuery.of(context).size;"
        ]
    },
    "icons":{
        "prefix":"icons",
        "body":[
            "Icon(Icons.$1),"
        ],
        "description": ""
    },
    "text":{
        "prefix":"text",
        "body":[
            "Text('$1'),"
        ],
        "description": ""
    },
    "navigate": {
        "prefix": "navigate",
        "body": [
            "Navigator.of(context).push(",
            "    MaterialPageRoute(builder: (context) {",
            "    return $1();",
            "}),",
        ],
        "description": ""
    },
    "pop":{
        "prefix":"pop",
        "body":[
            "Navigator.of(context).pop();"
        ],
        "description": ""
    },
    "if": {
        "prefix": "if",
        "body": [
            "if ($1) {",
            "  $2",
            "}",
            "",
        ],
        "description": ""
    },
    "ife": {
        "prefix": "ife",
        "body": [
            "if ($1) {",
            "  $2",
            "} else {",
            "",
            "}",
        ],
        "description": ""
    },
    "switch": {
        "prefix": "switch",
        "body": [
            "switch ($1) {",
            "  case $2:",
            "    break;",
            "  case $3:",
            "    break;",
            "  default:",
            "    break;",
            "}",
        ],
        "description": ""
    },
    "for": {
        "prefix": "for",
        "body": [
            "for (var i = 0; i < $1; i++) {",
            "  $2",
            "}",
        ],
        "description": ""
    },
    "forin": {
        "prefix": "forin",
        "body": [
            "for (final $1 in $1s) {",
            "  $2",
            "}",
        ],
        "description": ""
    },
    "class": {
        "prefix": "class",
        "body": [
            "class ${1:Name} {",
            "  $2",
            "}"
        ],
        "description": "",
    },
    "test": {
        "prefix": "test",
        "body": [
            "test('$1', () {",
            "  $2",
            "});"
        ],
        "description": "",
    },
    "group": {
        "prefix": "group",
        "body": [
            "group('$1', () {",
            "  $2",
            "});"
        ],
        "description": "",
    },
    "Theme.of(context).colorScheme": {
        "prefix": "the.color",
        "body": [
            "Theme.of(context).colorScheme.$1",
        ],
        "description": ""
    },
    "Theme.of(context).textTheme": {
        "prefix": "the.text",
        "body": [
            "Theme.of(context).textTheme.$1",
        ],
        "description": ""
    },
    "expanded": {
        "prefix": "expanded",
        "body": [
            "const Expanded(child: SizedBox()),",
        ],
        "description": ""
    },
}

個々の解説

登録した内容についての一部を簡単に解説しておきます。

stfh, stlh:

Stateful版のStatefulHookConsumerWidgetとStateless版のHookConsumerWidgetの作成支援です。
ページ作成時に利用できます。

VSCode拡張機能のFlutter Riverpod Snippetsを使っても同じようなことができますが、そのあと毎回importしたり、Scaffoldでラップしたりと面倒なので作成しました。

notif:

これを使えば、StateNotifierクラスと、グローバルで定義するStateNotifierProviderのインスタンス作成を支援してくれます。

接尾辞は以下のルールに従っています。チームで採用している形に合わせてご利用ください。

対象クラス クラス名 グローバル変数宣言
StateNotifierProvider 接尾辞「Notifier」 接尾辞「StateProvider」
StateNotifierが管理するStateクラス 接尾辞「State」 ---

入力後、Tabキーを押せばグローバルで定義するStateNotifierProviderのインスタンス名を入力できます。

viewmodel:

(2022/10/25)まったく使ってないので削除しました。

controllerp:

StateNotifierクラスをControllerという名称で作成される方はこちらをご利用ください。

接尾辞は以下のルールに従っています。

対象クラス クラス名 グローバル変数宣言
StateNotifierProvider 接尾辞「Controller」 接尾辞「ControllerProvider」
StateNotifierが管理するStateクラス 接尾辞「State」 ---

repo:

Repositoryの抽象クラスと実装クラス、グローバルで定義するProviderクラスのインスタンス作成支援です。

ここはひとまず、内部メモリを使用した例で登録していますが、ここは何らかの技術基盤に置き換え必要かと思うので、適宜変更ください。

frf:

Freezedクラスの作成支援です。
Flutter freezed Helpers」より若干使いやすくしたつもりです。

refw:

buildメソッド内でProviderで管理するStateを監視する際に使用するref.watch()の作成支援です。

refn:

buildメソッド内でProviderが管理するメソッドを使用したい場合の作成支援です。

uuid:

UUIDの作成支援です。
ランダムなIDを付加したいときにご利用ください。

sca:

とりあえずScaffoldを持った空のページを作成したいってときの作成支援です。

参考

https://qiita.com/12345/items/97ba616d530b4f692c97

https://migi.me/vsc_snippet/

以上です。これで効率化間違いなし!

コードスニペットは随時更新していきます。
ほかにもおすすめあればぜひ教えて頂けますと幸いです🤗

Discussion