AlexKorovyansky
AlexKorovyansky

Reputation: 4943

How to sync Flutter TextField with Firestore field?

I have task document in Firestore, which has todo field, and TextField in the Flutter UI.

Please advice how to make the textfield synced with the todo field, i.e.

  1. Any time, text in the textfield is changed as user's typing, update the todo field with the just typed value.
  2. Any time, the todo field is updated (manually in the Firestore console or by other user), update the textfield with the most recent value.

Thanks!

Upvotes: 2

Views: 5916

Answers (2)

Jonathan
Jonathan

Reputation: 392

First of all, supply a TextEditingController to the TextField (take a look at this for a complete example).

For the first part of the question, you will need to provide a listener to the TextEditingController. This listener should trigger a function like so :

  Future<void> _updateTaskValue(String text) {
    Firestore().runTransaction((Transaction transaction) {
      Firestore.instance.document([PATH OF YOUR DOCUMENT]).updateData({"todo": text});
    });
  }

Assuming that text is the controller's text value. Note that runTransaction is used to avoid data concurrency.

For the second part of the question, you will have to listen to the document. To do so, declare into initState a StreamSubscription :

subscription = Firestore.instance.document("").snapshots().listen(
    (DocumentSnapshot snapshot) => this._onDatabaseUpdate(snapshot));

This subscription will trigger a function each time the content is updated (whether the current user updates the TextField, another users updates it, or manually from the back office).

The function called below simply updates the controller's text attribute with the new content :

void _onDatabaseUpdate(DocumentSnapshot snapshot) {
  setState(() {
    _controller.text = snapshot.data["todo"];
  });
}

For a complete example, see this gist.

Upvotes: 5

Ajay
Ajay

Reputation: 16300

  1. TextField onChanged updates the value in firebase.
  2. Listen for value change in firebase and update the value for TextField with TextEditingController.

Here is the code :

import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'TODO Screen',
      theme: ThemeData(
        primarySwatch: Colors.orange,
      ),
      home: TODOScreen(),
    );
  }
}

class TODOScreen extends StatefulWidget {
  @override
  _TODOScreenState createState() => _TODOScreenState();
}

class _TODOScreenState extends State<TODOScreen> {
  final _ref = FirebaseDatabase.instance.reference().child('todo_id').child('value');
  TextEditingController _todoController = new TextEditingController();

  StreamSubscription _subscription;

  @override
  void initState() {
    super.initState();
    _subscription = _ref.onValue.listen((data) {
      String value = data.snapshot.value as String ?? "";
      updateOnChanged(value);
    });
  }

  saveOnChanged(String value) async {
    await _ref.set(value);
  }

  updateOnChanged(String value) async {
    setState(() {
      _todoController.value = _todoController.value.copyWith(
        text: value,
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TODO Screen'),
      ),
      body: Center(
        child: Container(
          padding: EdgeInsets.all(10.0),
          child: TextField(
            decoration: InputDecoration(labelText: "TODO"),
            maxLines: 5,
            onChanged: saveOnChanged,
            controller: _todoController,
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    _todoController.dispose();

    if (_subscription != null) _subscription.cancel();

    super.dispose();
  }
}

Hope it hepls!

Upvotes: 1

Related Questions