Flutter - Firestore - List String to be from collection field and update fire store once "Save Setting' is pressed

Viewed 43

I have two questions:

1. I need to build a List String form the documents using the email as the selection criteria , so I the query is

  Future<List<dynamic>> getDevices() async {
    var firebaseUser = FirebaseAuth.instance.currentUser?.email;
    var query = await firestoreInstance
        .collection(devices)
        .where('email', isEqualTo: '$firebaseUser')
        .get();

    //This is to get the Document ID to a List String, but I actually want this List String to be a
    // List String of the deviceName
    List<String> _documentsIds = query.docs.map((doc) => doc.id).toList();
    return _documentsIds;
  }

How do I map the deviceName to the list? Currently I map the doc.id

2. From the function “Future<List> getDevices()” the DropdownButton “Safegaurd Devices “ gets build.

When you select a Device it queries Firestore to get the value of startHour (using Function Future<DocumentSnapshot<Map<String, dynamic>>> getDeviceSettings() ) that is used to update the NumberPicker.

Now when you change the value using the NumberPicker, because it is a Future it keeps on calling the function Future<DocumentSnapshot<Map<String, dynamic>>> getDeviceSettings()

How do I stop it calling the function getDeviceSettings? As I want to only update Firestore once I press Save Settings.

enter image description here

enter image description here

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import 'package:safegaurd/constants.dart';
import 'package:safegaurd/components/buttons_navigate.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:numberpicker/numberpicker.dart';

class Test2 extends StatefulWidget {
  const Test2({Key? key}) : super(key: key);
  static const String id = 'test_2';

  @override
  State<Test2> createState() => _Test2State();
}

class _Test2State extends State<Test2> {
  final _auth = FirebaseAuth.instance;
  User? loggedInUser;

  final firestoreInstance = FirebaseFirestore.instance;
  final String devices = 'devices';

  bool showSpinner = false;

  String? deviceName;
  int startHour = 0;
  String? selectedDeviceValue;
  final bool checkOne = false;
  Color sDeviceAlways = Colors.white54;
  Color sDeviceTime = Colors.white54;
  FontWeight sDeviceAlwaysW = FontWeight.normal;
  FontWeight sDeviceTimeW = FontWeight.normal;

  @override
  void initState() {
    super.initState();
    getCurrentUser();
  }

  void getCurrentUser() async {
    try {
      final user = await _auth.currentUser;
      if (user != null) {}
      ;
    } catch (e) {
      print(e);
    }
  }

  Future<List<dynamic>> getDevices() async {
    var firebaseUser = FirebaseAuth.instance.currentUser?.email;
    var query = await firestoreInstance
        .collection(devices)
        .where('email', isEqualTo: '$firebaseUser')
        .get();

    //This is to get the Document ID to a List String, but I actually want this List String to be a
    // List String of the deviceName
    List<String> _documentsIds = query.docs.map((doc) => doc.id).toList();
    //List<String> _documentsIds = query.docs.map((doc) => ['deviceName']).toList<String>;
    return _documentsIds;
  }

  Future<DocumentSnapshot<Map<String, dynamic>>> getDeviceSettings() async {
    var deviceSetting = await firestoreInstance
        .collection(devices)
        .doc(selectedDeviceValue)
        .get();

    startHour = deviceSetting.data()!['startHour'];
    deviceName = deviceSetting.data()!['deviceName'];

    print(deviceSetting.data());
    print('deviceName: $deviceName');
    print('startHour: $startHour');

    return deviceSetting;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          leading: null,
          actions: [
            IconButton(
                onPressed: () {
                  _auth.signOut();
                  Navigator.pop(context);
                },
                icon: Icon(Icons.close))
          ],
          title: const Text('Device Selection page'),
        ),
        body: ModalProgressHUD(
          inAsyncCall: showSpinner,
          child: Container(
              child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const SizedBox(
                height: 40,
                child: Text(
                  'SAFEGAURD',
                ),
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Container(
                    width: 380,
                    padding: const EdgeInsets.symmetric(horizontal: 24.0),
                    decoration: BoxDecoration(
                        border: Border.all(
                            color: Colors.lightBlueAccent, width: 1.0),
                        borderRadius: kBorderRadius),
                    child: DropdownButtonHideUnderline(
                        child: FutureBuilder<List>(
                            future: getDevices(),
                            builder: (BuildContext context,
                                AsyncSnapshot<List<dynamic>> snapshot) {
                              if (!snapshot.hasData) {
                                return const Text(
                                  'Waiting Devices',
                                );
                              } else {
                                return DropdownButton(
                                  borderRadius: kBorderRadius,
                                  iconSize: 40,
                                  elevation: 16,
                                  hint: const Text(
                                    'Safegaurd Devices',
                                  ),
                                  onChanged: (value) {
                                    setState(() {
                                      selectedDeviceValue = value.toString();
                                      setState(() {
                                        selectedDeviceValue;
                                        getDeviceSettings();
                                      });
                                    });
                                  },
                                  value: selectedDeviceValue,
                                  items: snapshot.data
                                      ?.map((_documentsIds) =>
                                          DropdownMenuItem<String>(
                                            value: _documentsIds,
                                            child: Text(_documentsIds),
                                          ))
                                      .toList(),
                                );
                              }
                            })),
                  )
                ],
              ),
              SizedBox(
                height: 5,
              ),
              // This Future is to update startHour according to selectedDeviceValue selected
              // The problem is every time you change the NumberPicker it recalls from Firesore the value of
              // startHour, it is suppose to update the startHour and only store it in Firestore once you
              // press 'Save Settings'
              FutureBuilder(
                  future: getDeviceSettings(),
                  builder: (BuildContext context,
                      AsyncSnapshot<DocumentSnapshot<Map<String, dynamic>>>
                          snapshot) {
                    if (!snapshot.hasData) {
                      return spinnerBuild ();
                    } else {
                      return spinnerBuild ();
                    }
                  }),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Buttons_Navigate(
                    colour: Colors.teal,
                    title: 'Save Settings',
                    onPressed: () {},
                    width: 200,
                  ),
                ],
              ),
            ],
          )),
        ));
  }

Widget spinnerBuild (){
  return Column(
    children: [
      Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Column(
            children: [
              Container(
                width: 180,
                decoration: BoxDecoration(
                    border: Border.all(
                        color: Colors.lightBlueAccent,
                        width: 1.0),
                    borderRadius: kBorderRadius),
                child: Column(
                  crossAxisAlignment:
                  CrossAxisAlignment.center,
                  children: [
                    Container(
                      padding: const EdgeInsets.fromLTRB(
                          5, 10, 5, 1),
                      child: const Text(
                        'Start Time',
                      ),
                    ),
                    Container(
                      padding: const EdgeInsets.fromLTRB(
                          1, 1, 25, 1),
                      child: NumberPicker(
                        value: startHour,
                        minValue: 0,
                        maxValue: 24,
                        onChanged: (value) => setState(
                                () => startHour = value),
                      ),
                    )
                  ],
                ),
              )
            ],
          )
        ],
      ),
    ],
  );
}
}
1 Answers

For the first question, I think you could use List<String> devicesNames = query.docs.map((doc['deviceName']) => deviceName).toList();

let me know if this works

For the second question, I didn't really get what do you want, could you explain more?

Related