虎視眈々と

Flutter × Firebaseを研究するアプリエンジニア

Firebase Cloud FunctionsでJsonを返す

Cloud FunctionsからJsonを返す方法

下の実装では別のAPIを叩いて受け取った結果をjsonにして返却している。

const functions = require('firebase-functions');
const request = require('request');

exports.onBooksGenre = functions.https.onRequest((req, res) => {

    var headers = {
    'Content-Type':'application/json'
    }

    var options = {
    url: "URL",
    method: 'GET',
    headers: headers,
    json: true
    }

    return request(options, (error, response, body) => {
        if (error || response.statusCode !== 200) {
            res.json(error).end();
            return;
        }
        res.json(body).end();
    });
});

Goでhttpリクエストしてjsonを受け取ってコマンドラインに出力する

下記のコードでいけた

package main

import (
  "fmt"
  "io/ioutil"
  "net/http"
)

func main() {
  client := &http.Client{}
  req, err := http.NewRequest("GET", "リクエストURL", nil)

  if (err != nil) {
    fmt.Println("error occur")
  }

  resp, err := client.Do(req)

  if(err != nil) {
    fmt.Println("error occur")
  }

  defer resp.Body.Close()

  body, err := ioutil.ReadAll(resp.Body)

  if(err != nil) {
    fmt.Println("error occur")
  }

  fmt.Println(string(body))
}

RecyclerViewでアイテムのクリックイベントを受け取る

フラグメントにinterfaceを実装する。

 interface ItemListEventListener {
      fun onTapItem(view: View, id: Int)
  }

fragmentでimplementして実装を書く

ここではinterfaceの実装と、adapterにイベントリスナーの実装を渡している

 class ItemListFragment : Fragment(), ItemListEventListener {  
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View? {
 
         binding = FragmentItemListBinding.inflate(inflater, container, false)
 
         setupLayout()
 
         return binding.root
     } 
     override fun onTapItem(view: View, id: Int) {
         // タップイベントがあるとここが呼ばれる
     }
     
     private fun setupLayout() {
         itemAdapter = ItemListAdapter(this)
         val manager = ItemLayoutManager(itemAdapter, requireContext())
 
         binding.productList.run {
             layoutManager = manager
             adapter = itemAdapter
         }
      }
 }

adapterで引数として受け取る

 class ItemListAdapter(private val eventListener: ItemListEventListener) : ListAdapter<ProductListItem, RecyclerView.ViewHolder>(DiffUtilCallback) {
 
 // なんかの実装

adapter側でタップイベントを設定してリスナーとして渡す

 override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
         if (holder is ProductItemHolder) {
             val item = getItem(position)
             holder.binding.model = item
             holder.binding.productItem.setOnClickListener {
                 eventListener.onTapItem(it, item.id)
             }
          }
 }

これでタップイベントを受け取れる

参考

y-anz-m.blogspot.com

AndroidのNavigationを使って遷移先にパラメーターを渡す

Navigationについてはこちら

developer.android.com

渡す側

渡す側はbundleに引数を設定して、画面遷移するときに渡す

 val bundle = Bundle()
 bundle.putInt("id", id)
 Navigation.findNavController(view).navigate(R.id.action_item_list, bundle)

受け取る側

受け取る側は onActivityCreated で受け取るのがいい

 override fun onActivityCreated(savedInstanceState: Bundle?) {
     super.onActivityCreated(savedInstanceState)     
     val id = arguments?.getInt("id")
     print(id)
 }

参考

medium.com

FlutterとFirebase Authを使ってメールアドレス認証をする

FirebaseとFlutterの接続方法は下記

firebase.google.com

下記のプラグインを導入

pub.dartlang.org

導入したら下記のコードでログインできる

Future<FirebaseUser> _handleSignUp(String email, String password) async {
    final FirebaseAuth _auth = FirebaseAuth.instance;
    final FirebaseUser user = await _auth.createUserWithEmailAndPassword(
        email: email, password: password);
    return user;
  }

FlutterでBottom Sheetを出す

下記の方法で行けた

  _showUserInfo(User user, BuildContext context) {
    showModalBottomSheet(
        context: context,
        builder: (_) {
          return Container(
            width: 300,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                ListTile(
                  title: const Text(
                    "Kyat app",
                    style: const TextStyle(fontWeight: FontWeight.bold),
                  ),
                  trailing: CupertinoButton(
                      onPressed: () {
                        Navigator.pop(context);
                      },
                      child: const Text("cancel")),
                ),
                Container(
                  width: 100,
                  height: 100,
                  child: CachedNetworkImage(
                    imageUrl: user.iconUrl,
                  ),
                ),
                const SizedBox(
                  height: 20,
                ),
                Center(child: Text(user.name)),
                Container(
                  margin: const EdgeInsets.all(10),
                  child: Row(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[],
                  ),
                ),
              ],
            ),
          );
        });
  }

Flutterでドロワーを出す

ドロワーを出すだけなら1行でいけます。

return Scaffold(
      appBar: _appbar(),
      drawer: Drawer(),
)

上の記事のような昔のGmailみたいなドロワーは下記のコードで出せる

 Widget _drawerHeader() {
    return Drawer(
      child: ListView(
        children: <Widget>[
          UserAccountsDrawerHeader(
            accountName: const Text(
              "shogo.yamada",
              style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
            accountEmail: const SizedBox(),
            currentAccountPicture: CircleAvatar(
              child: Text(
                "A",
                style: TextStyle(fontSize: 40.0),
              ),
            ),
          ),
        ],
      ),
    );
  }

FlutterでBLoCのテストを書く

FlutterでBLoCのテストを書く方法について

BLoCって何?って方は下記の記事をご参照ください。

www.shogogeek.com

BLoCクラス

まずはBlocクラス

class HomeBloc {
  final UserRepositoryInterface userRepository;

  HomeBloc({
    @required this.userRepository,
  }) {
    _getUserInfo();
  }

  StreamController<User> _userController = StreamController<User>();

  StreamSink<User> get _userSink => _userController.sink;

  Stream<User> get userStream => _userController.stream;

  void dispose() {
    _userController.close();
  }

  _getUserInfo() async {
    _userSink.add(await userRepository.getUser());
  }
}

このクラスからRepositoryを設定されているとき userRepository からデータが返ってきた時に userStream にデータがaddされるかテストするコードを書きます。

テストを書く

今回はRepositoryをモック化しています。

void main() {
  UserRepositoryInterface repository;

  setUp(() {
    repository = FakeUserRepository();
  });

  HomeBloc createBloc() {
    return HomeBloc(
        userRepository: repository);
  }

  test("user stream test", () async {
    User user = await repository.getUser();
    HomeBloc bloc = createBloc();

    bloc.userStream.listen((User result) {
      expect(result.uid, user.uid);
    });
  });

bloc.userStream にデータがくるのを listen してデータがきたら結果が正しいかチェックしています。

FlutterにFirebase Crashlyticsを導入する (Android編)

FlutterにFirebase プロジェクトを追加

下記の手順で導入しましょう。

firebase.google.com

gradleを編集

android/build.gradle に下記を追加

buildscript {
    ext.kotlin_version = '1.3.20'
    repositories {
        google()
        jcenter()
        // ↓追加
        maven { url 'https://maven.fabric.io/public' }
    }

    dependencies {
        // ↓追加
        classpath 'io.fabric.tools:gradle:1.27.1'
        classpath 'com.android.tools.build:gradle:3.3.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.google.gms:google-services:4.2.0'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        ↓追加
        maven {
            url 'https://maven.google.com/'
        }
    }
}

app/build.gradleを編集

app/build.gradle に下記を追加

apply plugin: 'io.fabric'

これで動くはずです。

動かない時

動かない時は下記をご確認ください。

www.shogogeek.com

FlutterでCrashlyticsが動かない時

FlutterでFirebase Crashlyticsがビルドエラーになって動かなかったが下記の方法で解決した。

android/gradle/gradle-wrapper.properties に下記を追加すると直る。

#Sat Mar 02 00:07:33 JST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
// ↓を追加する
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip

Incompatible types: ○ and △ がでるとき

sealed を使って型の確認をするときに下記のエラーで表示できなくなった。

Incompatible types: ○ and △

みたいなエラーがでるときは下記の方法で使える ちゃんと継承させれば解決できる

 sealed class ListItem {
 
     data class ItemList(
         val list: List<ProductItem>
     ) : ListItem()
 
     object Title: ListItem()
 
     object Progress : ListItem()
 }

Androidでテキストを太字にする

下記の方法でいけた

簡単にいうと、 android:textStyle="bold" を設定するだけで太字になった。

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="@string/item_list_title"
        android:textStyle="bold"
        android:textSize="20sp" />

Firebaseのコンソールでユーザーを招待する

ユーザーの招待は下記の方法でできる。

コンソールに移動 ↓

下記のボタンをタップ

f:id:superman199323:20190226145622p:plain

そこから「ユーザーと権限」ボタンをタップすればできる。

Flutter(Dart)でシングルトン

class AnalyticsUtil {

  static FirebaseAnalytics _analytics;

  static FirebaseAnalytics getInstance() {
    if (_analytics == null) {
      return FirebaseAnalytics();
    }

    return _analytics;
  }
}