Firestoreについて

データモデル

大きく3つの要素を指定することになります。

  1. データ
    • フィールドと値のペアを複数持っている(オブジェクト)
  2. ドキュメント
    • 1つのデータを持っている
    • ドキュメントのIDを指定する必要がある
  3. コレクション
    • 複数のドキュメントを持っている
    • コレクションのIDを指定する必要がある

      f:id:yukking3:20210911162826p:plain

コレクションの取得

FirebaseFirestore.instance.collection('books').get();

get();を使うことでコレクションやドキュメントを取得できる。

 

ドキュメントの取得

FirebaseFirestore.instance.collection('books').doc('id').get();

 booksというコレクションの中のドキュメントのidを取得できる。

 

FirebaseFirestore.instance.collection('books').doc('id').collection('orders').doc('id_123').collection('orders').doc('id_123').get();

この場合はコレクションIDとドキュメントIDを指定して取得 できる。

 

値の追加

2つの方法がある。

・Add()

・Set()

 

add()の特徴

コレクションに対して行うメソッド

追加する値に指定することで、ドキュメントを追加することができる。

FirebaseFirestore.instance

.collection('books')

.add({      'title': title,      'author': author,    });

set()の特徴

任意のドキュメントに対して追加して・一括更新をすることができる。
コレクションが存在しない場合は自動的に作成される

FirebaseFirestore.instance
                    .collection('users') // コレクションID
                    .doc('id_abc') // ドキュメントID
                    .set({'name': '鈴木', 'age': 40}); // データ

 

値の更新

FirebaseFirestore.instance

.collection('books')

.update({      'title': title,      'author': author,    });

 

値の削除

FirebaseFirestore.instance

.collection('books')

.doc(id)

.delete();

 

Providerとは

Providerとは

Flutter開発において、初心者→中級者にステップアップする上で、Providerを使うというのは避けては通れない。Google社は、BloC Patternを使うことが2018年ぐらいに推奨していましたが、その後、アプリの規模に対して学習コストが高かったり、ここまで大きなアーキテクチャが本当に必要なのか論などがあり、最近はprovider推奨らしい。現在は、Riverpodと言う新しい Provider があ出ている。Provider と作者が一緒なので、今後はRiverpodになるかもしれない。

 

結局providerってなに?

アプリの状態管理をしてくれるツールである。

情報のやり取りをしてくれるライブラリである。

 

Providerは、変更を自動で察知して、アプリ全体に変更があった事を伝える重要な機能を提供してくれる便利なパッケージ です。

 

SetStateとの比較

初級→setState

中級→Provider

 

SetStateを使った状態管理は、

【Stateless】一度表示したら、変更することがない。 → 原則上書きしない状態

【Stateful】何度か表示を変更する。 → 上書き可能な状態

 

setStateの問題点は2つ。

・パフォーマンス
・メンテナンス性

 

setStateの場合は、変更する毎に、画面全体を更新してしまいます。無関係なところも再描画されてるため無駄が多く、パフォーマンスが悪くなりがち。複雑なアプリになるとパフォーマンスの部分で大きな問題が出てくるらしい。

 

Providerは表示内容やデータの変更を、アプリ全体に効率的に伝える仕組みを提供する。Providerを使う事で、プログラム内の離れた箇所へダイレクトに変更を通知して、該当箇所のみデータや表示内容を変更する。一方でsetStateでは限られた範囲にしか変更を通知できず、さらにその範囲全てを無条件に書き換えてしまう効率の悪いものでした。

要するに、通知を簡単に効率的にやってくれるらしい。

 

MVVM(モデル・ビュー・ビューモデル) の場合

モデルの業務:データの保存や読み出しに関連すること

ビューモデルの業務:モデルとビューのやり取りの仲介に関すること

ビューの業務:データの表示、アプリの見た目に関すること

これらの業務間で、データが変更された、データを変更して、データをください、などをやり取りする時にProviderの機能を使って連携するのです。

Providerは、あくまで状態が変化を伝える方法。

 

Providerの仕組み

監視するために「ChangeNotifierProvider」 というWidgetが一番最初のメイン関数が呼ばれるタイミングで組み込まれてます。

void main()  {
  runApp(ChangeNotifierProvider(
      create: (_) => TopScreenModel(),
      child: MyApp()));
}

これでによって、アプリ全体の変更を察知くれるわけです。

 

Providerの3つの役目

・変更を通知する。→ Provider.of<TopScreenModel>(context, listen: false)
・変更をあった事を全体に通知する。→ notifyListeners();
・変更があったら、その分を変更する。→Consumer

 

ちなみにProviderを使うと、StatefulWidgetを使わずStatelessWidgetで書くことができる。

FlutterにProviderを追加する

lib
│ main.dart

├─models
│      logic.dart

└─view
        ui.dart

 

補足:

設計(アーキテクト)であるMVVMパターンについて

f:id:yukking3:20210904182149p:plain

f:id:yukking3:20210904184311p:plain

 

Providerを使ったMVVMパターンの実装方法

状態管理の仕組み。

f:id:yukking3:20210904182232p:plain

f:id:yukking3:20210904183912p:plain

 

クラス間のDependecy Injectionについて

f:id:yukking3:20210904182325p:plain

 

 

 

 

 

 

Flutter基本構造(書き方)

 

今回はアプリの基本構造(書き方)について紹介します。

以下のコードを解説したいと思います。

import 'package:flutter/material.dart'

void main() {

     runApp(MyApp());
}

//起動時にmain関数呼び出し

//main関数からrunApp関数を呼び出すことで、MyAppが実行される。

 

class MyApp extends StatelessWidget {
    @override

    Widget build(BuildContext context) {
        return MaterialApp(

            home: Scaffold(
                appBar: AppBar(
                    title: Text("AppBar"),
                ),
                body: Center(
                    child: Text("Body"),
                ),
            ),
        );
    }
}

//StatelessWidgetを継承したクラス

//build関数を用意し、MaterialAppインスタンスをreturnする

//homeに実際にアプリ内に表示するWidgetを設定する

アプリの完成イメージ

f:id:yukking3:20210904121502j:plain

 

全体の大まかな流れ

1. 起動時に最初に呼び出されるのは、main関数。
2. main関数からrunApp関数を呼び出すことで、アプリが実行される。
3. runApp関数に引数で指定した、StatelessWidgetを継承したクラスがアプリ本体のUIとなる。
4. StatelessWidgetを継承したクラスには、build関数を用意し、MaterialAppインスタンスをreturnする。
5. MaterialAppに用意されている、homeに実際にアプリ内に表示するWidgetを設定する。

 

f:id:yukking3:20210904130053j:plain

 

1.最初に呼び出されるのは、main関数。

最初にimportしている「material.dart」は、マテリアルデザインのUIがまとめられたパッケージです。次に、main関数の中でアプリを実行する為のrunApp関数を呼び出します。※runApp関数は引数必須の為、カッコの中に実行するクラス入れないとビルドエラーになります。

import 'package:flutter/material.dart';
 
void main() {
    runApp();
}

 

2. main関数からrunApp関数を呼び出しアプリが実行される。3. StatelessWidgetを継承したクラスがアプリ本体のUIとなる。

StatelessWidgetを継承したMyAppクラスを作成し、runApp引数に設定します。
ここで指定したMyAppクラスがアプリ本体となります。今回はMyAppとしましたが、クラス名は何でも大丈夫ですが基本的にはMyAppです。※StatelessWidgetはbuild関数が必須の為、この時点もまだビルドエラーになります。

import 'package:flutter/material.dart';
 
void main() {
    runApp(MyApp());
}
 
class MyApp extends StatelessWidget {
}

4. build関数を用意し、MaterialAppインスタンスをreturnする。appbarとbodyの部分

StatelessWidetに必要なbuild関数をオーバーライドし、
MaterialAppインスタンスをreturnします。

この時点でビルドエラーは消えますが、
肝心のUI部分を記述していない状態です。

  Widget build(BuildContext context) {
        return MaterialApp();

が骨格となる枠を作ると言うことですね。

import 'package:flutter/material.dart';

void main() {
    runApp(MyApp());
}

class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        return MaterialApp();
    }
}

5. homeに実際にアプリ内に表示するWidgetを設定する。

MaterialAppに用意されている、「home」に実際にアプリ内に表示するWidgetを設定します。今回はScaffoldという、一般的なデザインの土台となるWidgetを使用しています。

import 'package:flutter/material.dart';

void main() {
    runApp(MyApp());
}

class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            home: Scaffold(
                appBar: AppBar(
                    title: Text("AppBar"),
                ),
                body: Center(
                    child: Text("Body"),
                ),
            ),
        );
    }
}

Flutterでの基本部品

Flutterアプリでは、アプリケーション全体管理するための「MaterialApp」と画面全体を構成するための「Scaffold」という部品が用意されています。

 

MaterialAppは、Flutterアプリケーションの全体を管理するものです。主に全体の状態監視、そして、全体にかかわるプロパティの管理しています。

Scaffoldは「足場」という意味になりますが、画面全体を管理する部品になります。

f:id:yukking3:20210904133302p:plain

実際のアプリ作り

f:id:yukking3:20210904134252p:plain

 

 

 

余談

オブジェクト指向

初心でも理解しやすいように解説すると。。。。

オブジェクト指向プログラミング言語では

・クラス
インスタンス
・オブジェクト

の3つの用語が頻繁に登場します。この3つは、とても大事です。

f:id:yukking3:20210904124620g:plain

f:id:yukking3:20210904125433p:plain

f:id:yukking3:20210904125517p:plain

 

オーバーライド

オーバーライドは一言で言うと「子クラスで親クラスのメソッドを再定義することなんだな~」と、お考えください。

書き換えた後のプログラムを実行すると、画面には

ピヨシウム光線! と表示されます。

https://wa3.i-3-i.info/word138.html

//ピヨ太ママクラス
class PiyotaMama{
  public void hissatuwaza(){
    System.out.println("ぴよぴよ波!");
  }
}

 

//ピヨ太君クラス
class Piyota extends PiyotaMama{
  public void hissatuwaza(){
    System.out.println("ピヨシウム光線!");
  }
}

 

//主処理
public class test{
  public static void main(String[] args){
    //ピヨ太君クラスのインスタンス「piyota」を生成
    Piyota piyota = new Piyota();

 

    //必殺技発射!
    piyota.hissatuwaza();
  }
}

 

 

【ytmusic】基本操作

import requests
from ytmusicapi import YTMusic
import json

ytmusic = YTMusic("headers_auth.json")


#プレイリスト作成→曲検索→検索結果をプレイリストに登録
playlistId = ytmusic.create_playlist('test', 'test description')
search_results = ytmusic.search(query="colors 宇多田ヒカル")
print(search_results)

try:
ytmusic.add_playlist_items(playlistId, [search_results[0]['videoId']])
print(search_results[0]['videoId'])
except NameError: # 処理する例外の型が指定されている
print('検索結果なし!') # ここが実行される
except: # NameError 以外はここで処理
print('不明のエラー')

 

【検索の場合】

YTMusic.search(query: strfilter: str = Nonelimit: int = 20ignore_spelling: bool = False) → List[Dict[KT, VT]]

Search YouTube music Returns results within the provided category.

Parameters:
  • query – Query string, i.e. ‘Oasis Wonderwall’
  • filter – Filter for item types. Allowed values: songsvideosalbumsartistsplaylistscommunity_playlistsfeatured_playlistsuploads. Default: Default search, including all types of items.
  • limit – Number of search results to return Default: 20
  • ignore_spelling – Whether to ignore YTM spelling suggestions. If True, the exact search term will be searched for, and will not be corrected. This does not have any effect when the filter is set to uploads. Default: False, will use YTM’s default behavior of autocorrecting the search.

実際の実行方法

 

search_results = ytmusic.search(query="colors 宇多田ヒカル")

print(search_results)

 

実際の実行方法

search_results = ytmusic.search(query="colors 宇多田ヒカル",filter=None,limit=1)
#リミットが壊れているのか使えない。
search_results = ytmusic.search(query="colors 宇多田ヒカル",filter="videos",limit=0)

 

実際の実行方法

search_results = ytmusic.get_history()
print(search_results)

 

 

 

【ytmusic】プレイリスト作成→曲検索→検索結果をプレイリストに登録

import requests
from ytmusicapi import YTMusic
import json

ytmusic = YTMusic("headers_auth.json")


#プレイリスト作成→曲検索→検索結果をプレイリストに登録
playlistId = ytmusic.create_playlist('test', 'test description')
search_results = ytmusic.search(query="colors 宇多田ヒカル")
print(search_results)

try:
ytmusic.add_playlist_items(playlistId, [search_results[0]['videoId']])
print(search_results[0]['videoId'])
except NameError: # 処理する例外の型が指定されている
print('検索結果なし!') # ここが実行される
except: # NameError 以外はここで処理
print('不明のエラー')

 

 playlistId = ytmusic.create_playlist('test', 'test description')

プレイリスト作成する。プレイリスト名:test、 ディスクリプション名: test description'

 

search_results = ytmusic.search(query="colors 宇多田ヒカル")

search_results に検索結果を格納する。

 

ytmusic.add_playlist_items(playlistId, [search_results[0]['videoId']])
プレイリストに検索結果の1番目を登録する。

 

current user Section 11, Lecture 52

ログインしてるのにログイン画面に飛べるのを処理する。 f:id:yukking3:20180506195653p:plain

f:id:yukking3:20180506195714p:plain

以下のコードを設定するだけ良い。
if current_user.is_authenticated:はもしすでにログイン済みの場合はフラッシュメッセージを表示する。

@at.route('/login', methods=['GET', 'POST'])
def do_the_login():
    if current_user.is_authenticated:
        flash('you are already logged-in')
        return redirect(url_for('main.display_books'))

logging users out Section 11, Lecture 51

layout.htmlに以下を追加で記述して、各ファンクションとリンクさせる。

<li><a href="{{ url_for('main.display_books') }}"> Home </a></li>
<li><a href="{{ url_for('authentication.register_user') }}"> Register </a></li>
<li> {% if current_user.is_authenticated %}
        <a href="{{ url_for('authentication.log_out_user') }}"> SignOut </a>
     {% else %}
        <a href="{{ url_for('authentication.do_the_login') }}"> SignIn </a>
     {% endif %}
</li>

ログアウト処理を作る。routes.pyに作る。 def log_out_user作って。リダイレクトさせるだけ

@at.route('/logout')
@login_required
def log_out_user():
    logout_user()
    flash('Logged out Successfully')
    return redirect(url_for('main.display_books'))

f:id:yukking3:20180506195344p:plain