使用 Flutter 将表单上传到 Firestore

Posted

技术标签:

【中文标题】使用 Flutter 将表单上传到 Firestore【英文标题】:Upload Form to Firestore with Flutter 【发布时间】:2018-12-05 21:32:56 【问题描述】:

我对如何在我的颤振应用中将表单上传到 Firestore 感到困惑。我已经阅读了许多教程,并且能够登录 Firebase 并将 Firestore 中的数据检索到我的颤振应用程序中,但我不知道如何将包含大量文本字段的表单上传到 Firestore。

示例:文档标题“书籍” 文本字段将包括:标题、作者等。

*** // 下面是我的页面,它确实从 Firestore 导入数据(这有效)。我添加了一个按钮,该按钮转到另一个页面,我试图在其中添加要上传到 Firestore 的表单(这不起作用)。页面加载有效,但未设置上传到 Firestore。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';





class FireStore extends StatelessWidget 
  FireStore(this.auth, this.onSignedOut);
  final BaseAuth auth; 
  final VoidCallback onSignedOut;

  void _signOut () async 
    try 
      await auth.signOut();
      onSignedOut();
     catch (e) 
      print (e);
    
  





  @override
    Widget build(BuildContext context) 
      return new Scaffold(
        appBar: new AppBar(
          title: new Text ('Welcome'),
          actions: <Widget>[

          new FlatButton(
            child: new Text('Logout', style: new TextStyle(fontSize: 17.0, color: Colors.white)),
            onPressed: _signOut
          )
          ]
        ),
        body: new StreamBuilder(
          stream: Firestore.instance.collection('books').snapshots(),
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) 
        if (!snapshot.hasData) return CircularProgressIndicator();
        return new GridView.count(
          crossAxisCount: 2,
          childAspectRatio: 1.0,
          padding: const EdgeInsets.all(4.0),
          mainAxisSpacing: 4.0,
          crossAxisSpacing: 4.0,


          children: snapshot.data.documents.map((DocumentSnapshot document) 
            return new ListTile(
              title: new Text(document['title']),
              subtitle: new Text(document['author']),

            );

          ).toList(),
        );
      ,
    ),
    floatingActionButton: new FloatingActionButton(
      elevation: 0.0,
      child: new Icon(Icons.add),
      backgroundColor: new Color(0xFFE57373),
      onPressed: ()
        Navigator.push(
        context,
        new MaterialPageRoute(builder: (context) => new UploadFormField()),
        );

      
    )
      );
      

 



// UPLOAD TO FIRESTORE



class UploadFormField extends StatefulWidget 
  @override
  _UploadFormFieldState createState() => _UploadFormFieldState();


class _UploadFormFieldState extends State<UploadFormField> 
  GlobalKey<FormState> _key = GlobalKey();
  bool _validate = false;
  String title, author;
  @override
Widget build(BuildContext context) 

  return MaterialApp(
    home: new Scaffold(
      appBar: new AppBar(
        title: new Text('Upload'),
      ),
      body: new SingleChildScrollView(
       child: new Container(
         margin: new EdgeInsets.all(15.0),
         child: new Form(
           key: _key,
           autovalidate: _validate,



         child: FormUI(

         )),
       ),


      ),

      ),
    );
  
  Widget FormUI() 
    return new Column(
      children: <Widget>[
        new TextFormField(
          decoration: new InputDecoration(hintText: 'Title'),
          validator: validateTitle,
          onSaved: (String val) 
            title = val;
          
        ),

        new TextFormField(
          decoration: new InputDecoration(hintText: 'Author'),
          validator: validateAuthor,
          onSaved: (String val) 
            author = val;
          
        ),
        new SizedBox(height: 15.0),
        new RaisedButton(onPressed: _sendToServer, child: new Text('Upload'),
        )
      ],
    );
  
  String validateTitle (String value) 
    String pattern = r' (^[a-zA-Z ]*$)';
    RegExp regExp = new RegExp(pattern);
    if (value.length == 0)
             return 'Title is required';

            else if (!regExp.hasMatch(value)) 
             return "Title must be a-z and A-Z";
           

           return null;
  

  String validateAuthor (String value) 
    String pattern = r' (^[a-zA-Z ]*$)';
    RegExp regExp = new RegExp(pattern);
    if (value.length == 0)
             return 'Author is required';

            else if (!regExp.hasMatch(value)) 
             return "Author must be a-z and A-Z";
           

           return null;
  


  _sendToServer()
    if (_key.currentState.validate() )
      //No error in validator
      _key.currentState.save();
      print ("Title $title");
      print ("Author $author");
     else 
      // validation error
      setState(() 
              _validate = true;
            );
    

  

【问题讨论】:

发布您开始使用的代码。 这是当前的代码/表单(我已经简化了)。如果您向下滚动大约一半,您将看到“UploadFormField”类。这就是我假设我需要上传 FIrestore 数据的地方。 【参考方案1】:

我能够通过以下方式解决问题:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';





class FireStore extends StatelessWidget 
  FireStore(this.auth, this.onSignedOut);
  final BaseAuth auth; 
  final VoidCallback onSignedOut;

  void _signOut () async 
    try 
      await auth.signOut();
      onSignedOut();
     catch (e) 
      print (e);
    
  





  @override
    Widget build(BuildContext context) 
      return new Scaffold(
        appBar: new AppBar(
          title: new Text ('Welcome'),
          actions: <Widget>[

          new FlatButton(
            child: new Text('Logout', style: new TextStyle(fontSize: 17.0, color: Colors.white)),
            onPressed: _signOut
          )
          ]
        ),
        body: new StreamBuilder(
          stream: Firestore.instance.collection('books').snapshots(),
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) 
        if (!snapshot.hasData) return CircularProgressIndicator();
        return new GridView.count(
          crossAxisCount: 2,
          childAspectRatio: 1.0,
          padding: const EdgeInsets.all(4.0),
          mainAxisSpacing: 4.0,
          crossAxisSpacing: 4.0,


          children: snapshot.data.documents.map((DocumentSnapshot document) 
            return new ListTile(
              title: new Text(document['title']),
              subtitle: new Text(document['author']),

            );

          ).toList(),
        );
      ,
    ),
    floatingActionButton: new FloatingActionButton(
      elevation: 0.0,
      child: new Icon(Icons.add),
      backgroundColor: new Color(0xFFE57373),
      onPressed: ()
        Navigator.push(
        context,
        new MaterialPageRoute(builder: (context) => new UploadFormField()),
        );

      
    )
      );
      

 



// UPLOAD TO FIRESTORE



class UploadFormField extends StatefulWidget 
  @override
  _UploadFormFieldState createState() => _UploadFormFieldState();


class _UploadFormFieldState extends State<UploadFormField> 
  GlobalKey<FormState> _key = GlobalKey();
  bool _validate = false;
  String title, author;

  @override
Widget build(BuildContext context) 

  return MaterialApp(
    home: new Scaffold(
      appBar: new AppBar(
        title: new Text('Upload'),
      ),
      body: new SingleChildScrollView(
          child: new Container(
            margin: new EdgeInsets.all(15.0),
            child: new Form(
              key: _key,
              autovalidate: _validate,
              child: FormUI(),

            ),
          ),
        ),
      ),
    );
  
  Widget FormUI() 
    return new Column(
      children: <Widget>[
        new TextFormField(
          decoration: new InputDecoration(hintText: 'Title'),
          validator: validateTitle,
          onSaved: (String val) 
            title = val;
          
        ),

        new TextFormField(
          decoration: new InputDecoration(hintText: 'Author'),
          validator: validateAuthor,
          onSaved: (String val) 
            author = val;
          
        ),
        new SizedBox(height: 15.0),
        new RaisedButton(onPressed: _sendToServer, child: new Text('Upload'),
        )
      ],
    );
  
  String validateTitle(String value) 
    String patttern = r'(^[a-zA-Z ]*$)';
    RegExp regExp = new RegExp(patttern);
    if (value.length == 0) 
      return "Title is Required";
     else if (!regExp.hasMatch(value)) 
      return "Title must be a-z and A-Z";
    
    return null;
  

   String validateAuthor(String value) 
    String patttern = r'(^[a-zA-Z ]*$)';
    RegExp regExp = new RegExp(patttern);
    if (value.length == 0) 
      return "Author is Required";
     else if (!regExp.hasMatch(value)) 
      return "Author must be a-z and A-Z";
    
    return null;
  


 _sendToServer()
    if (_key.currentState.validate() )
      //No error in validator
      _key.currentState.save();
      Firestore.instance.runTransaction((Transaction transaction) async 
        CollectionReference reference = Firestore.instance.collection('books');

        await reference.add("Title": "$title", "Author": "$author");
      );
     else 
      // validation error
      setState(() 
              _validate = true;
            );
    

  

【讨论】:

【参考方案2】:

Flutter 的 Firestore 插件将 Map 作为 setData 或 add 函数的输入,因此无论您在类对象或其他变量中拥有什么数据,您只需将其转换为 map(或嵌套数据结构的嵌套 map)并提供给setData 或添加函数作为输入以保存在 Firestore 中。对于类,颤振示例显示主要在类中实现了“toMap()”函数,该函数仅返回所需映射结构中的所有数据类对象,其对应的“fromMap(Map mapData)”将用于从稍后从 Firestore 获取数据时映射。

例如。

await Firestore.instance
    .collection(TBL_USERS)
    .add(
      "type": "Dog",
      "age": 6,
      "breed": "abc",
    );

或者由于它的map,你可以直接使用任何字符串(只要它在map中唯一)作为key

例如。

await Firestore.instance
    .collection(TBL_USERS)
    .add(
      "Dog": 
              "age": 6,
              "breed": "abc",
             ,
      "Cat" : 
              "age": 2,
              "breed": "xyz",
             ,
    );

【讨论】:

我将把它放在我的代码中的什么位置?我的“上传”部分在“UploadFormField”类中。 另外,我使用了 Firestore 中的示例,文档名称为“books”,文本字段中包含“Title”和“Author”。

以上是关于使用 Flutter 将表单上传到 Firestore的主要内容,如果未能解决你的问题,请参考以下文章

使用flutter将多张图片上传到firestore并获取它的下载URL并将所有URL保存到firebase

Flutter:添加动态 TextFormField 以将数据上传到列表

Flutter 向 API 发送数据(图像和表单)

将 cloud_firestore 添加到 pubspec Flutter 后无法为模拟器构建应用程序

Flutter - 将表单绑定到模型

如何使用 Flutter 将图像上传到 Firebase