ubuntuにflutterをインストールでデプロイ

Flutter公式のインストールページを参考に、snapでflutterをインストールします。
https://docs.flutter.dev/get-started/install/linux

$ sudo snap install flutter –classic
$ flutter sdk-path

続いてflutterで開発したgithubのソースコードをリポジトリからcloneします。
$ git clone https://github.com/hpscript/flutter_test.git
$ cd flutter_test

$ flutter build web
あれ、githubのものをサーバ上でbuildするのではないの?
それとも、開発環境でbuildしたものをpushしてデプロイするのか?

.gitignoreでbuildをコメントアウトしてpush, pullしてサーバに置くとbuild/webをドキュメントルートとして表示できるようになる。

**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
#/build/

なるほど、flutterのデプロイについては何となく理解した。
dartで書いて、buildすれば良いのね。 良く出来ているわ。

【Flutter】firebaseへのデプロイ

### build
firebaseへデプロイする前に、ファイルをcompileします
$ flutter build web

Fireforceのhosting機能を有効化
プロジェクトのcurrent directoryで
$ sudo npm install -g firebase-tools –force
$ firebase login
プロジェクトの初期化
$ firebase init
=> publicのdirectoryを ./build/web/ で設定する

firebase
$ firebase deploy

webアプリをbuildする
$ flutter build web
$ firebase deploy

firebase hostingを無効化する
$ firebase hosting:disable

OKKKKKKKKKKKKKKKK

【flutter】session_storageでセッション管理

Flutterでsession_storageのパッケージを利用します。
https://pub.dev/packages/session_storage

pubspec.yaml

dependencies:
  session_storage:
  form_field_validator:
  flutter:
    sdk: flutter

main.dart

import 'package:session_storage/session_storage.dart';
// 省略
            onTap: (int value) {
              if (value == 1)
                SessionStorage()..addAll({'language': 'english'});
                Navigator.push(
                  context,
                  MaterialPageRoute(builder:
                      (context)=> SecondScreen()),
                );
            }
// 省略

        body: Center(
          child: Container (
            child: Text(
                'Here: ${session['language']}',
                style: const TextStyle(fontSize:32.0)),
          ),
        ),

セッションとDBが使えるとかなり幅が広がる

【flutter】firestoreでフィールドの値があるかDartで判定

firestoreにフィールドの値が挿入されているか確認し、値の有無によってflutter側でリダイレクト処理を行う
=> 単純にif nullで処理できる

    final snapshot = await firestore.collection('mydata').doc('12345678').get();
    final Map<String, dynamic>? mydata = snapshot.data();
    if (mydata?['hoge']  != null) {
      msg = '値があります';
    } else {
      msg = '値がありません';
    }

これにアラートメッセージを追加する

    if (mydata?['hoge']  != null) {
      msg = '値があります';
    } else {
      showDialog(
        context: context,
        builder: (BuildContext context) => AlertDialog(
          title: Text("値がありません"),
          content: Text("値がありません"),
        )
      );

エリアを指定してアラート文を表示が一般的かもしれないが、アラートダイアログなどを一発で実装できるのもFlutterの良いところですね。

【flutter】firestoreから1件取り出す

docでuidを指定した後に、mapで取得する。その際に、Mapではなく、Map?とする。また、mapは mydata[‘name’] とするとエラーになるので、mydata?[‘name’]とする。

    String? msg = '';
    FirebaseFirestore firestore = FirebaseFirestore.instance;
    final snapshot = await firestore.collection('mydata').doc('12345678').get();
    final Map<String, dynamic>? mydata = snapshot.data();
    msg = mydata?['name'];
    _controller.text = msg!;

firestoreではなく、flutterの中でmap型を指定する場合は以下のように書いてOK

    final Map<String, String> frameworks = {
      'Flutter' : 'Dart',
      'Rails' : 'Ruby',
    };

コレクションを複数指定する場合とは書き方が大分異なる

    var msg = '';
    FirebaseFirestore firestore = FirebaseFirestore.instance;
    final snapshot = await firestore.collection('mydata').orderBy('name', descending: false).get();
    snapshot.docChanges.forEach((element) {
      final name = element.doc.get('name');
      final mail = element.doc.get('mail');
      final age = element.doc.get('age');
      msg += "${name} (${age}) <${mail}>\n";
    });
    _controller.text = msg;

【flutter】firestoreを更新する方法

uidを指定して、setを実行するとcollection自体の値が全て更新されてしまう。

    var msg = _controller.text;
    final data = {
      'address': msg,
    };
    FirebaseFirestore firestore = FirebaseFirestore.instance;
    final snapshot = await firestore.collection('mydata').doc('12345678').set(data);

コレクション自体を残す場合はsetではなく、updateを使用する

    var msg = _controller.text;
    final data = {
      'address': msg,
    };
    FirebaseFirestore firestore = FirebaseFirestore.instance;
    final snapshot = await firestore.collection('mydata').doc('12345678').update(data);

【Flutter】別ページへのデータの引き渡し

引数に値を入れる

  static var _prev = 'ページ1';
  
  void buttonPressed() {
    setState(() {
      // Navigator.pushNamed(context, '/complete');
      Navigator.push(context, MaterialPageRoute(builder: (context)=>CompleteScreen(_prev)),);
    });
  }

複数画面の入力フォームなどはどのような設計にするかは要検討か

【Flutter】Scaffoldの中でFormを使いたい

https://pub.dev/packages/form_field_validator

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
      title: Text(widget.title),
      ),
      body: Form (
          key: _formKey,

Widget build(BuildContext context)は複数記述できないので、body: Formと書く

そうすると、TextFormFieldが使えるようになる

            Padding(
              padding: EdgeInsets.all(10.0),
              child: TextFormField(
                controller: _id_controller,
                style: TextStyle(
                    fontSize: 28.0,
                    color: const Color(0xffFF0000),
                    fontWeight: FontWeight.w400,
                    fontFamily:"Roboto"),
                validator: idValidator,
              ),
            ),

【Flutter】バリデーション その2

class MyCustomFormState extends State<MyCustomForm>{
  final _formKey = GlobalKey<FormState>();
  final textValidator = MultiValidator([
    RequiredValidator(errorText: '入力必須の項目です。'),
    MinLengthValidator(8, errorText: '8文字以上で入力してください。'),
  ]);

  @override
  Widget build(BuildContext context){
    return Form(
      key: _formKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          TextFormField(
                  validator: textValidator,
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 16.0),
            child: ElevatedButton(
              onPressed:() {
                if(_formKey.currentState!.validate()){
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(content: Text('送信完了')),
                  );
                }
              },
              child: const Text('送信'),
            )
          )
        ]
      )
    );
  }
}

【Flutter】バリデーション

class MyCustomFormState extends State<MyCustomForm>{
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context){
    return Form(
      key: _formKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          TextFormField(
            validator: (value){
              if(value == null || value.isEmpty){
                return 'テキストを入力してください';
              }
            },
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 16.0),
            child: ElevatedButton(
              onPressed:() {
                if(_formKey.currentState!.validate()){
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(content: Text('送信完了')),
                  );
                }
              },
              child: const Text('送信'),
            )
          )
        ]
      )
    );
  }
}

フォームを一意に認識するためのキー

final _formKey = GlobalKey<FormState>();
Form(
     key: _formKey,
     )

入力がない場合テキストを返す

if (_formKey.currentState!.validate()) {
                 ScaffoldMessenger.of(context).showSnackBar(
                   const SnackBar(content: Text('送信完了')),
                 );
               }