Masahiro Okubo

Rails devise token auth + ReactNative || flutterの雛形コード

Rails devise token auth + ReactNative || flutterの雛形コード

React Nativeとflutterの両方を使うことになったので、いつも使っているrails APIにつなげることができるようにしました。

※React Nativeは割と綺麗に書きましたが、flutterは最後のリファクタリングを行っていないのであまり綺麗ではありませんのでご注意ください。

とりあえずイメージ↓

React Native

Github repo:
https://github.com/hiyashikyuri/event-app-client

flutter

Github repo:
https://github.com/hiyashikyuri/event_app_flutter

Rails devise token auth based API

Github repo:
https://github.com/hiyashikyuri/event-app-api

どちらを使うべきかの個人的感想

結論、flutter

Javaに近い文法でかなり学習コストが低い

昔、Android開発していたときにみたようなコードで、Javaやっていた人からするとかなり学習コスト低いと思います

@override
Widget build(BuildContext context) {

UI全てをDartで記述可能

リファクタリングが簡単です

最初は読みにくいですが、1日もすれば慣れます。

全てDartでまとまっているので、loop部分や長くなってしまったファイルを簡単に分割することが可能です。

return ListTile(
      title: Text(title),
      leading: CircleAvatar(
        backgroundImage: NetworkImage(imageUrl),
      ),
      trailing: Container(
        width: 100,
        child: Row(
          children: <Widget>[
            IconButton(
              icon: Icon(Icons.edit),
              onPressed: () {
                Navigator.of(context).pushNamed(EditProductScreen.routeName, arguments: id);
              },
              color: Theme.of(context).primaryColor,
            ),
            IconButton(
              icon: Icon(Icons.delete),
              onPressed: () async {
                try {
                  await Provider.of<Products>(context, listen: false)
                      .deleteProduct(id);
                } catch (error) {
                  scaffold.showSnackBar(
                    SnackBar(
                      content: Text('削除に失敗しました', textAlign: TextAlign.center,),
                    ),
                  );
                }
              },
              color: Theme.of(context).errorColor,
            ),
          ],
        ),
      ),
    );

drawerなどのUI

drawer等のUI系が一撃で作れます

こちらがイメージとコードです

// drawerのクラス
class AppDrawer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: Column(
        children: <Widget>[
          AppBar(
            title: Text('ナビゲーション'),
            automaticallyImplyLeading: false,
          ),
          Divider(),
          ListTile(
            leading: Icon(Icons.shop),
            title: Text('イベント一覧'),
            onTap: () {
              Navigator.of(context).pushReplacementNamed('/');
            },
          ),
          Divider(),
          ListTile(
            leading: Icon(Icons.edit),
            title: Text('自分の投稿一覧'),
            onTap: () {
              Navigator.of(context)
                  .pushReplacementNamed(UserProductsScreen.routeName);
            },
          ),
          Divider(),
          ListTile(
            leading: Icon(Icons.exit_to_app),
            title: Text('ログアウト'),
            onTap: () {
              Navigator.of(context).pop();
              Navigator.of(context).pushReplacementNamed('/');
              Provider.of<Auth>(context, listen: false).logout();
            },
          ),
        ],
      ),
    );
  }
}


// drawerを入れる時のコード
appBar: AppBar(
  title: Text(loadedProduct.title),
),
drawer: AppDrawer(),  // <= drawerに指定のクラスを入れるだけで作れる!!

React NativeでDrawerを実装したときはライブラリを探したり、コードをぐちゃぐちゃにしながら頑張って入れたので、それと比較すると比べ用もないぐらい簡単に実装できます

型がしっかりしているので安心感が凄まじい

実行前にIDEが型チェックのエラーを教えてくれるので、いつ爆発するかわからない怖さから結構開放されるかと思います。

class Products with ChangeNotifier {
  List<Product> _items = [];

  final String authToken;
  final int userId;

  Products(this.authToken, this.userId, this._items);

  List<Product> get items {
    return [..._items];
  }

  List<Product> get myItems {
    if (userId == null) {
      return [];
    }
    return _items.where((prodItem) => prodItem.user.id == userId).toList();
  }

  Product findById(int id) {
    return _items.firstWhere((prod) => prod.id == id);
  }

  Future<Map<String, String>> getAuthorization() async {
    final prefs = await SharedPreferences.getInstance();
    if (!prefs.containsKey('userData')) {
      return {};
    }
    final extractedUserData = json.decode(prefs.getString('userData')) as Map<String, Object>;

    return {
      'access-token': extractedUserData['access-token'],
      'client': extractedUserData['client'],
      'uid': extractedUserData['uid']
    };
  }
}

navigation

Navigator.of(context).pushNamed(EditProductScreen.routeName, arguments: id);

こんな感じのコードを入れるだけでどこでもUIを遷移させることができます。

モジュールも入れる必要がないので、まじで上記のコードだけで実装できて、動いたときは簡単すぎて感動しました。

アイコンが標準搭載

アイコンや色の指定がめちゃくちゃ簡単です。

IconButton(
  icon: Icon(Icons.edit), 
  color: Theme.of(context).primaryColor,
),

ホットリロード

本当に冗談抜きで秒で変更点が反映されます。

ストレスを感じなく、開発できるのでこれはポイントがかなり高いです。

flutterがおすすめだけどReact Nativeを使った方が良い人

  • スマホアプリを開発したことがない人
  • Java/Swiftがかけない人
  • Reactを使っている人

上記の方達はflutterよりもReact Nativeを利用した方がコストは低いと思います。

ただし、数ヶ月React Nativeを触った身からすると、

  • Iconでエラーがでたりするのが困る
  • nodeが重い
  • ホットリロードはflutterの方が早い
  • typescriptを利用しない場合は型チェックやメモリリークが怖い

など気になる点がそこそこあり、仮にJavaなどの静的言語を使ったことがなかったとしても勉強してからflutterを利用した方がメリットが多いのではないかな、と思っています。

まとめ

最終的にはプロジェクトの予算やチームのスキルセットによって選択されるFWも異なって来るかと思いますが、それでもflutterは書きやすくデザインセンスのない私でも簡単にそれっぽくできてしまうので、めちゃめちゃおすすめしたいです。

エラーとか指摘のポイントがあれば教えてください。

すぐに対応できるか分かりませんが、できるかぎり早めに修正します


関連記事

copyright© 2016-2021 Masahiro Okubo