【问题标题】:LiveQuery does not work, if there is ParseConnectivityProvider provided如果提供了 ParseConnectivityProvider,LiveQuery 不起作用
【发布时间】:2020-11-25 22:16:20
【问题描述】:

我正在开发一个 Flutter 应用程序,它由一个具有 Parse Server 背景的员工调度程序组成。该应用程序的工作原理如下:

1) 用户打开应用程序;
2)应用程序显示可用员工的卡片列表(如果服务器数据库中有一些)
3)用户可以编辑、确认或删除员工的可用性。

应用程序正在运行。这意味着我也可以从我的 Parse Server 和我的应用程序屏幕一样对数据进行 CRUD。

目标:数据库中的每一次数据更改都需要刷新(实时)用户屏幕,移除、删除或更新(更改的)数据本身。

我的想法:使用 parse_server_sdk: ^2.0.1 作为依赖项 (pubspec.yaml) 并实现实时查询(如 github 文档 here

问题:编译后,尽管我的 AndroidStudio 在视觉上没有崩溃,我的应用程序本身也没有崩溃,但它显示(日志)“LiveQuery 不起作用,如果提供了 ParseConnectivityProvider” .

没有问题(main.dart):

void main() async{

  runApp(MyApp());
}

导致问题:

void main() async{

//the following parameters are in other file (constants.dart)...they are working

  Parse().initialize(
    kParseApplicationId,
    kParseServerUrl,
    masterKey: kParseMasterKey,
    clientKey: kParseClientKey,
    debug: true,
    liveQueryUrl: kLiveQueryUrl,
    autoSendSessionId: true,
  );

  var dietPlan = ParseObject('DietPlan')
    ..set('Name', 'Ketogenic')
    ..set('Fat', 65);
  await dietPlan.save();

  var response = await dietPlan.save();
  if (response.success) {
    dietPlan = response.result;
  }

  //until here, the program works well.The data is inserted and retrieved, ok...but exactly here..the problem!

  final LiveQuery liveQuery = LiveQuery(); //from here..this is causing problems

  QueryBuilder<ParseObject> query =
  QueryBuilder<ParseObject>(ParseObject('DietPlan'))
    ..whereEqualTo('intNumber', 1);

  Subscription subscription = await liveQuery.client.subscribe(query);

  subscription.on(LiveQueryEvent.delete, (value) {
    print('*** DELETE ***: ${DateTime.now().toString()}\n $value ');
    print((value as ParseObject).objectId);
    print((value as ParseObject).updatedAt);
    print((value as ParseObject).createdAt);
    print((value as ParseObject).get('objectId'));
    print((value as ParseObject).get('updatedAt'));
    print((value as ParseObject).get('createdAt'));
  });

  runApp(MyApp());
}

我没有找到任何解决方案 googleing.Any。经过大量研究,我已经(深入研究库) Parse.Initialize(...) 将connectivityProvider 作为其参数之一,但我没有填写该参数。

另外,当我插入该行时,问题就开始了:

final LiveQuery liveQuery = LiveQuery();

有人可以帮我解决这个问题吗?在此先感谢

在下面,我展示了我所有的代码(main.dart):

// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:convert';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:intl/intl.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:marca_horario/model/data.dart';
import 'package:marca_horario/network_utils/data_utils.dart';
import 'package:http/http.dart';
import 'package:parse_server_sdk/parse_server_sdk.dart';
import 'package:marca_horario/constants.dart';

void myLQ() async {
  Parse().initialize(
    kParseApplicationId,
    kParseServerUrl,
    masterKey: kParseMasterKey,
    clientKey: kParseClientKey,
    debug: true,
    liveQueryUrl: kLiveQueryUrl,
    autoSendSessionId: true,
  );

  var dietPlan = ParseObject('DietPlan')
    ..set('Name', 'Ketogenic')
    ..set('Fat', 65);
  await dietPlan.save();

  var response = await dietPlan.save();
  if (response.success) {
    dietPlan = response.result;
  }

  final LiveQuery liveQuery = LiveQuery();

  QueryBuilder<ParseObject> query =
  QueryBuilder<ParseObject>(ParseObject('DietPlan'))
    ..whereEqualTo('intNumber', 1);

  Subscription subscription = await liveQuery.client.subscribe(query);

  subscription.on(LiveQueryEvent.delete, (value) {
    print('*** DELETE ***: ${DateTime.now().toString()}\n $value ');
    print((value as ParseObject).objectId);
    print((value as ParseObject).updatedAt);
    print((value as ParseObject).createdAt);
    print((value as ParseObject).get('objectId'));
    print((value as ParseObject).get('updatedAt'));
    print((value as ParseObject).get('createdAt'));
  });
}


void main() async{

  Parse().initialize(
    kParseApplicationId,
    kParseServerUrl,
    masterKey: kParseMasterKey,
    clientKey: kParseClientKey,
    debug: true,
    liveQueryUrl: kLiveQueryUrl,
    autoSendSessionId: true,
  );

  var dietPlan = ParseObject('DietPlan')
    ..set('Name', 'Ketogenic')
    ..set('Fat', 65);
  await dietPlan.save();

  var response = await dietPlan.save();
  if (response.success) {
    dietPlan = response.result;
  }

  final LiveQuery liveQuery = LiveQuery();

  QueryBuilder<ParseObject> query =
  QueryBuilder<ParseObject>(ParseObject('DietPlan'))
    ..whereEqualTo('intNumber', 1);

  Subscription subscription = await liveQuery.client.subscribe(query);

  subscription.on(LiveQueryEvent.delete, (value) {
    print('*** DELETE ***: ${DateTime.now().toString()}\n $value ');
    print((value as ParseObject).objectId);
    print((value as ParseObject).updatedAt);
    print((value as ParseObject).createdAt);
    print((value as ParseObject).get('objectId'));
    print((value as ParseObject).get('updatedAt'));
    print((value as ParseObject).get('createdAt'));
  });

  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        localizationsDelegates: [
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate
        ],
        supportedLocales: [const Locale('pt', 'BR')],
        home: Home()
    );
  }
}

class Home extends StatefulWidget {

  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {

  var _listTiles = List<String>();
  Color _iconColor = Colors.black;
  Color standardIconColor = Colors.black;
  Color alternateIconColor = Colors.green;
  TextEditingController _nameController = TextEditingController();
  var _scaffoldKey = new GlobalKey<ScaffoldState>();
  String standardTileTitle = "Adicione um horário disponível...";
  String _titleTile = "Adicione um horário disponível...";
  String _tileSubtitle = "Edite o nome do funcionário...";
  int _selectedIndexBottomNavBar = 0;
  final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = new GlobalKey<RefreshIndicatorState>();

  @override
  Widget build(BuildContext context) {
    return RefreshIndicator(
        key: _refreshIndicatorKey,
        onRefresh: () async {
          setState(() {

        });},
        child: Scaffold(
            key: _scaffoldKey,
            floatingActionButton: FloatingActionButton(
              onPressed: () {
                invokeDatePicker();
              },
              child: Icon(Icons.add),
            ),
            bottomNavigationBar: bottomNavigationBar(),
            appBar: AppBar(
              title: Text('Marca Horário'),
            ),
            // body: Center(
            //   child: Text('Hello World'),
            body: bodyStartScreen()
        ),
    );
  }


  Widget bottomNavigationBar(){

    void _onItemTapped(int index) {
      setState(() {
        _selectedIndexBottomNavBar = index;
      });
      print(_selectedIndexBottomNavBar);
      if(_selectedIndexBottomNavBar == 2){
        DatePicker.showDateTimePicker(context,
            showTitleActions: true,
            minTime: DateTime(2020, 1, 1),
            maxTime: DateTime(2021, 12, 31),
            onChanged: (date) {
              print('change $date');
            },
            onConfirm: (date) {
              print('confirm $date');
              _listTiles.add(DateFormat.yMMMEd('pt_BR').add_Hm().format(date).toString());
              _titleTile = DateFormat.yMMMEd('pt_BR').add_Hm().format(date).toString();

              setState(() {

              });
            },
            currentTime: DateTime.now(),
            locale: LocaleType.pt);
      }
    }

    return BottomNavigationBar(
      items: const <BottomNavigationBarItem>[
        BottomNavigationBarItem(
          icon: Icon(Icons.home),
          label: 'Home',
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.person),
          label: 'Funcionário',
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.access_time),
          label: 'Marcar',
        ),
      ],
      currentIndex: _selectedIndexBottomNavBar,
      selectedItemColor: Colors.amber[800],
      onTap: _onItemTapped,
    );
  }

  Widget bodyStartScreen(){
    return Column(
      children: [
        //the main title of the screen
        Padding(
          padding: EdgeInsets.all(16.0),
          child: Text("Horários Possíveis",
            style: TextStyle(
                fontSize: 18.0
            ),
          ),
        ),
        //gets available employees and datetimes from the server
        FutureBuilder(builder: (context,snapshot){
          if (snapshot.data != null) {
            List<Data> dataList = snapshot.data;

            return Expanded(
              child: ListView.builder(
                itemBuilder: (_, position) {
                return Card(
                  child: ListTile(
                    title: Text(dataList[position].dateTime),
                    subtitle: Text(dataList[position].employee),
                    trailing: Row(
                      mainAxisSize: MainAxisSize.min,
                      children: <Widget>[
                        IconButton(icon: Icon(Icons.edit), onPressed: () {
                          //Show dialog box to update item
                          showUpdateDialog(dataList[position]);
                        }),
                        IconButton(icon: Icon(Icons.check_circle, color: Colors.green,), onPressed: () {

                        }),
                        //Show dialog box to delete item
                        IconButton(icon: Icon(Icons.delete), onPressed: () {
                          deleteData(dataList[position].objectId);
                        }),
                      ],
                    ),
                  ),
                );
              },
                itemCount: dataList.length,
              ),
            );

          } else {
            return Center(
              child: CircularProgressIndicator(),
            );
          }
        },
          future: getDataList(),
        ),
        Divider(
          color: Colors.black,
        ),
        scheduleTile()
      ],
    );
  }

  void invokeDatePicker(){
    DatePicker.showDateTimePicker(context,
        showTitleActions: true,
        minTime: DateTime(2020, 1, 1),
        maxTime: DateTime(2021, 12, 31),
        onChanged: (date) {
          print('change $date');
        },
        onConfirm: (date) {
          print('confirm $date');
          _listTiles.add(DateFormat.yMMMEd('pt_BR').add_Hm().format(date).toString());
          _titleTile = DateFormat.yMMMEd('pt_BR').add_Hm().format(date).toString();

          setState(() {

          });
        },
        currentTime: DateTime.now(),
        locale: LocaleType.pt);
  }

  void showUpdateDialog(Data data) {

    _nameController.text = data.employee;

    showDialog(context: context,
        builder: (_) => AlertDialog(
          content: Container(
            width: double.maxFinite,
            child: TextField(
              controller: _nameController,
              decoration: InputDecoration(
                labelText: "Atualizar funcionário disponível",
              ),
            ),
          ),
          actions: <Widget>[
            FlatButton(onPressed: () {
              Navigator.pop(context);
              data.employee = _nameController.text;
              updateData(data);
            }, child: Text("Atualizar")),
            FlatButton(onPressed: () {
              Navigator.pop(context);
            }, child: Text("Cancelar")),
          ],
        )
    );

  }

  Widget scheduleTile(){
    return Padding(
      padding: EdgeInsets.only(bottom: 80.0),
      child: Card(
        color: Colors.grey,
        child: ListTile(
          title: Text(_titleTile),
          subtitle: Text(_tileSubtitle),
          trailing: Row(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              IconButton(
                icon: Icon(
                  Icons.edit,
                  color: standardIconColor,
                  size: 20.0,
                ),
                onPressed: () {
                  setState(() {
                    employeeAvailable();
                  });
                },
              ),
              IconButton(
                icon: Icon(
                  Icons.check_circle_outline,
                  color: _iconColor,
                  size: 20.0,
                ),
                onPressed: () {
                  setState(() {
                    (_titleTile != standardTileTitle) ? confirmSchedule() : fillTimeDialog();
                  });
                },
              )
            ],
          ),
        ),
      ),
    );
  }

  void fillTimeDialog(){
    showDialog(context: context,
        builder: (_) => AlertDialog(
          content: Container(
            width: double.maxFinite,
            child: Text("Insira o horário disponível!"),
          ),
          actions: <Widget>[
            FlatButton(onPressed: () {
              Navigator.pop(context);
            }, child: Text("OK")),
          ],
        )
    );
  }

  void employeeAvailable(){
    showDialog(context: context,
        builder: (_) => AlertDialog(
          content: Container(
            width: double.maxFinite,
            child: TextField(
              controller: _nameController,
              decoration: InputDecoration(
                labelText: "Funcionário",
              ),
            ),
          ),
          actions: <Widget>[
            FlatButton(onPressed: () {

              Navigator.pop(context);
              //addTodo();
              setState(() {
                _tileSubtitle = "Disponível: " + _nameController.text;
              });

            }, child: Text("Inserir")),
            FlatButton(onPressed: () {
              Navigator.pop(context);
              setState(() {
                _tileSubtitle = " ";
              });

            }, child: Text("Desfazer")),
          ],
        )
    );
  }

  void confirmSchedule(){
    showDialog(context: context,
        builder: (_) => AlertDialog(
          content: Container(
            width: double.maxFinite,
            child: Text("Confirma disponibilidade?")
          ),
          actions: <Widget>[
            FlatButton(onPressed: () {
              Navigator.pop(context);
              //addTodo();
              addData();
              setState(() {
                _iconColor = alternateIconColor;
                _tileSubtitle = "Disponível: " + _nameController.text;
              });

            }, child: Text("Confirma")),
            FlatButton(onPressed: () {
              Navigator.pop(context);
              setState(() {
                _iconColor = standardIconColor;
                _tileSubtitle = " ";
              });

            }, child: Text("Não")),
          ],
        )
    );
  }

  void addData() {

    _scaffoldKey.currentState.showSnackBar(SnackBar(content: Row(
      children: <Widget>[
        Text("Adicionando informações..."),
        CircularProgressIndicator(),
      ],
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
    ),
      duration: Duration(minutes: 1),
    ));

    Data data = Data(employee: _tileSubtitle, dateTime: _titleTile);

    DataUtils.addData(data)
        .then((res) {

      _scaffoldKey.currentState.hideCurrentSnackBar();

      Response response = res;
      if (response.statusCode == 201) {
        //Successful
        _nameController.text = "";

        _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Informações disponibilizadas!"), duration: Duration(seconds: 1),));

        setState(() {
          //Update UI
        });

      }

    });

  }

  void deleteData(String objectId) {

    _scaffoldKey.currentState.showSnackBar(SnackBar(content: Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children:  <Widget>[
        Text("Excluindo disponibilidade..."),
        CircularProgressIndicator(),
      ],
    ),
      duration: Duration(minutes: 1),
    ),);


    DataUtils.deleteData(objectId)
        .then((res) {

      _scaffoldKey.currentState.hideCurrentSnackBar();

      Response response = res;
      if (response.statusCode == 200) {
        //Successfully Deleted
        _scaffoldKey.currentState.showSnackBar(SnackBar(content: (Text("Disponibilidade excluída!")),duration: Duration(seconds: 1),));
        setState(() {

        });
      } else {
        //Handle error
      }
    });

  }

  void updateData(Data data) {

    _scaffoldKey.currentState.showSnackBar(SnackBar(content: Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        Text("Atualizando disponibilidade..."),
        CircularProgressIndicator(),
      ],
    ),
      duration: Duration(minutes: 1),
    ),);


    DataUtils.updateData(data)
        .then((res) {

      _scaffoldKey.currentState.hideCurrentSnackBar();

      Response response = res;
      if (response.statusCode == 200) {
        //Successfully Deleted
        _nameController.text = "";
        _scaffoldKey.currentState.showSnackBar(SnackBar(content: (Text("Disponibilidade atualizada!"))));
        setState(() {

        });
      } else {
        //Handle error
      }
    });

  }

  Future <List<Data>> getDataList() async{

    List<Data> dataList = [];

    Response response = await DataUtils.getDataList();
    print("Code is ${response.statusCode}");
    print("Response is ${response.body}");

    if (response.statusCode == 200) {
      var body = json.decode(response.body);
      var results = body["results"];

      for (var data in results) {
        dataList.add(Data.fromJson(data));
      }

    } else {
      //Handle error
    }

    return dataList;
  }
}

data_utils.dart:

import 'dart:convert';

import 'package:http/http.dart';
import 'package:marca_horario/model/data.dart';
import 'package:marca_horario/constants.dart';

class DataUtils {

  //static final String _baseUrl = "https://parseapi.back4app.com/classes/";
  static final String _baseUrl = baseUrl;

  //CREATE
  static Future<Response> addData(Data data) async {
    String apiUrl = _baseUrl + "Data";

    Response response = await post(apiUrl,
      headers: {
        'X-Parse-Application-Id': kParseApplicationId,
        'X-Parse-REST-API-Key': kParseRestApiKey,
        'Content-Type': 'application/json'
      },
      body: json.encode(data.toJson()),
    );

    return response;
  }

  //READ
  static Future getDataList() async{

    String apiUrl = _baseUrl + "Data";

    Response response = await get(apiUrl, headers: {
      'X-Parse-Application-Id' : kParseApplicationId,
      'X-Parse-REST-API-Key' : kParseRestApiKey,
    });

    return response;
  }

  //UPDATE
  static Future updateData(Data data) async{

    String apiUrl = _baseUrl + "Data/${data.objectId}";

    Response response = await put(apiUrl, headers: {
      'X-Parse-Application-Id' : kParseApplicationId,
      'X-Parse-REST-API-Key' : kParseRestApiKey,
      'Content-Type' : 'application/json',
    },
        body: json.encode(data.toJson())
    );

    return response;
  }

  //DELETE
  static Future deleteData(String objectId) async{

    String apiUrl = _baseUrl + "Data/$objectId";

    Response response = await delete(apiUrl, headers: {
      'X-Parse-Application-Id' : kParseApplicationId,
      'X-Parse-REST-API-Key' : kParseRestApiKey,
    });

    return response;
  }
}

【问题讨论】:

    标签: android-studio flutter dart parse-server


    【解决方案1】:

    问题解决了!
    我的问题是:import 'package:parse_server_sdk/parse_server_sdk.dart';
    需要替换为:import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; 在 pubspec.yaml 中,只需要使用对应的依赖项:
    parse_server_sdk_flutter: ^2.0.1

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-14
      • 1970-01-01
      • 2016-05-22
      • 2012-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多