LoginSignup
1
6

More than 1 year has passed since last update.

Flutter Providerを使ってFormの値を保持し、外部ファイルで保存する処理を実行(Firestoreを使用)

Posted at

はじめに

この記事はProviderを使って、Formとは別のファイルにあるボタンを押して「保存」する方法をざっくりと解説します。

外部ファイルで保存の処理を実行したい!!

これは一体どう言うことか?
下記の画面を見ながら説明する!
スクリーンショット 2021-06-28 23.36.09.jpg

ファイルの構造

ファイルの構造は以下👇のようなイメージ

 └ form_screen.dart
     ├ form_widget.dart
     └ save_button.dart 

form_widget.dartが「作品の見所」から①、②、③の部分であり、ユーザーが入力をするFormの部分

save_button.dartFloatingActionButtonの部分で、このボタンを押すとユーザーがform_widget.dartで入力した内容が保存される

入力された値を保持するには?

入力した値を保持するには、以下のように_form.currentState.save()というような実装内容にしないといけませんよね

final _form = GlobalKey<FormState>();
String _userId;
String _password;

// 省略

body: Form(
      key: _form, // 追加
      child: Column(
        children: <Widget>[
          TextFormField(
            decoration: InputDecoration(labelText: 'id'),
            onSaved: (value) {
              _userId = value;
            },
          ),
          TextFormField(
            decoration: InputDecoration(labelText: 'password'),
            onSaved: (value) {
              _password = value;
            },
          ),
          TextButton(
            child: Text('Save'),
            onPressed: () {
              _form.currentState.save(); // これが重要!!!
              print(_userId);
              print(_password);
            },
          ),

しかし今回はFormと保存するボタンが別のファイルにあります!!

さてどうしましょう?
と言うのが、今回の肝になる内容です。

色々とやり方はあるとは思いますが、今回はProviderを使って実現しようと思います。

Providerを使って実装

・ProviderとFirebaseの導入は省略します。:bow:

・実際のソースは映画のAPIを使っており、今回の記事の内容とは関係のないソースが一部あります。適当に省略しているので、謎のコードがあっても気にしないでください。あくまで「参考」という事で!

ファイルの構造

あくまでイメージです。実際のソースでは差異があります。

lib/
 ├ main.dart
 ├ models
 ├    └ mypage_movie_model.dart
 ├ providers
 │ └ mypage_movie_provider.dart
 ├ widgets
   ├ save_movie_text.dart
   └ mypage_movie_form.dart

mypage_movie_model.dart

Providerの中でFormの入力データを扱うために、データの型を定義します。

import 'package:cloud_firestore/cloud_firestore.dart';

class MyPageMovieModel {
  MyPageMovieModel(DocumentSnapshot doc) {
    movieId = doc.id;
    title = doc.data()['title'];
    pointText1 = doc.data()['pointText1'];
    pointText2 = doc.data()['pointText2'];
    pointText3 = doc.data()['pointText3'];
  }
  String movieId;
  String title;
  String pointText1;
  String pointText2;
  String pointText3;
}

mypage_movie_provider.dart

Formの値を保持 + Firestoreに値を保存する処理を書きます。

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/mypage_movie_model.dart';

class MyPageMovieProvider with ChangeNotifier {
  MyPageMovieModel myPageMovie;
  String point1Text = '';
  String point2Text = '';
  String point3Text = '';
  final uid = FirebaseAuth.instance.currentUser.uid;
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;

  // Formに入ってる内容をFirestoreに保存する
  Future editMyMovies(String movieId) async {
    final movieRef = _firestore.doc('users/${uid}/movies/${movieId}');
    await movieRef.update({
      'id': int.parse(movieId),
      'point1': point1Text,
      'point2': point2Text,
      'point3': point3Text,
    });
  }
}

updateメソッドを使っていますが、保存と同じ意味です。

point1Textという値が、Providerを使うことによって「Formがあるファイル」と「保存ボタンがあるファイル」で共通化されるような、そんな認識かと!

save_movie_text.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'mypage_movie_form.dart';
import '../../providers/mypage_movie_provider.dart';

class SaveMovieText extends StatelessWidget {
  final int id;
  SaveMovieText(this.id);

  @override
  Widget build(BuildContext context) {
    return Consumer<MyPageMovieProvider>(builder: (context, model, child) {
      return FloatingActionButton(
        onPressed: () async {
          print('保存' + id.toString());
          await model.editMyMovies(id.toString());
          // FloatingActionButtonを押したら、Formに入力されてる内容が出力される!
          print(model.point1Text);
          print(model.point2Text);
          print(model.point3Text);
        },
        child: const Icon(Icons.save),
        backgroundColor: Theme.of(context).primaryColor,
      );
    });
  }
}

model.point1Textというのが、mypage_movie_provider.dartで定義してある「point1Text」の事!

mypage_movie_form.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../common_UI/space_box.dart';
import '../../providers/mypage_movie_provider.dart';

class MyPageMovieForm extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Consumer<MyPageMovieProvider>(builder: (context, model, child) {
      return Form(
        child: ListView(
          padding: EdgeInsets.only(top: 10),
          shrinkWrap: true,
          children: <Widget>[
            Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Container(
                  width: 50,
                  height: 50,
                  decoration: BoxDecoration(
                      border: Border.all(
                        width: 1,
                        color: Colors.grey,
                      ),
                      shape: BoxShape.circle),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text('1'),
                    ],
                  ),
                ),
                SpaceBox.width(10),
                Expanded(
                  child: TextFormField(
                    decoration: InputDecoration(labelText: '見所1'),
                    keyboardType: TextInputType.multiline,
                    maxLines: null,
                    textInputAction: TextInputAction.newline,
                    onChanged: (value) {
                      // ここでmypage_movie_provider.dartのpoint1Textに、入力した内容を反映してる
                      model.point1Text = value;
                    },
                  ),
                ),
              ],
            ),
            Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Container(
                  width: 50,
                  height: 50,
                  decoration: BoxDecoration(
                      border: Border.all(
                        width: 1,
                        color: Colors.grey,
                      ),
                      shape: BoxShape.circle),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text('2'),
                    ],
                  ),
                ),
                SpaceBox.width(10),
                Expanded(
                  child: TextFormField(
                    decoration: InputDecoration(labelText: '見所2'),
                    keyboardType: TextInputType.multiline,
                    maxLines: null,
                    textInputAction: TextInputAction.newline,
                    onChanged: (value) {
                      model.point2Text = value;
                    },
                  ),
                ),
              ],
            ),
            Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Container(
                  width: 50,
                  height: 50,
                  decoration: BoxDecoration(
                      border: Border.all(
                        width: 1,
                        color: Colors.grey,
                      ),
                      shape: BoxShape.circle),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text('3'),
                    ],
                  ),
                ),
                SpaceBox.width(10),
                Expanded(
                  child: TextFormField(
                    decoration: InputDecoration(labelText: '見所3'),
                    keyboardType: TextInputType.multiline,
                    maxLines: null,
                    textInputAction: TextInputAction.newline,
                    onChanged: (value) {
                      model.point3Text = value;
                    },
                  ),
                ),
              ],
            ),
          ],
        ),
      );
    });
  }
}

model.point1Textというのが、mypage_movie_provider.dartで定義してある「point1Text」の事!

main.dart

import 'package:provider/provider.dart';
import './providers/mypage_movie_provider.dart';

 // 省略
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (context) => MyPageMovieProvider(),
        ),
      ],
      child: MaterialApp(
       // 省略

あんまりよくわかってませんが、MultiProviderのchildにMaterialAppを置くことで、全て?がProviderの監視対象になるようです
(この認識が間違ってた教えて下さい:sweat:

最後に

これで
・Providerを使ってFormに入力された値を保持
・Formとは別のファイルにあるボタンから保存を実行

ができました!

最初は色々なファイルに無理やり値を渡して試してみましたが、結局うまくいかず。。。。
Providerを使えば良い感じに実現できました。

参考

1
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
6