javascript - 使用js.context.callMethod()调用方法时如何接收数据返回flutter?

标签 javascript flutter dart flutter-web

我正在使用 Flutter Web 构建一个 chrome 扩展,这只是一个简单的 ListView在主屏幕和基本 CRUD 操作上。我正在使用 dart:js包以从 JS 文件中调用方法,该文件在 Firebase 实时数据库上执行一些操作。
向数据库添加新条目是 工作通过add()方法调用。读取操作在 JS 文件中也可以正常工作。
我的主要问题是我应该如何 阅读 数据库信息为 JSON,解析并显示为 ListView在 flutter 。

这里是 main.dartAddAttendee.dart -

import 'dart:js' as js;
import 'dart:convert';

import 'package:flutter/material.dart';
import 'AddAttendee.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Google Flutter Meet Attendance',
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  List<Map> data;

  getAttendees() async {
    var obj = await js.context.callMethod('read');
    List<Map> users = (jsonDecode(obj) as List<dynamic>).cast<Map>();
    setState(() {
      data = users;
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Current Attendees"),
      ),
      body: data != null
          ? ListView.builder(
              padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
              itemCount: data.length,
              itemBuilder: (context, index) {
                return ListTile(
                  leading: Icon(Icons.person),
                  title: Text(data.toString()[index]), // I don't know how to read correctly
                );
              },
            )
          : Center(child: CircularProgressIndicator()),
      floatingActionButton: FloatingActionButton(
        tooltip: 'Add to Attendance',
        child: Icon(Icons.person_add),
        onPressed: () => Navigator.push(
          context,
          MaterialPageRoute(
            builder: (_) => AddAttendee(),
          ),
        ),
      ),
    );
  }
}
import 'dart:js' as js;

import 'package:flutter/material.dart';

class AddAttendee extends StatefulWidget {
  @override
  _AddAttendeeState createState() => _AddAttendeeState();
}

class _AddAttendeeState extends State<AddAttendee> {
  final _formKey = GlobalKey<FormState>();
  final TextEditingController _textController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Add to Attendance List"),
      ),
      body: Form(
        key: _formKey,
        child: SingleChildScrollView(
          padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
          child: TextFormField(
            autocorrect: false,
            autofocus: true,
            controller: _textController,
            onFieldSubmitted: (value) => _textController.text = value,
            decoration: InputDecoration(labelText: "Name"),
            keyboardType: TextInputType.text,
            textInputAction: TextInputAction.next,
            textCapitalization: TextCapitalization.sentences,
            validator: (value) {
              if (value.isEmpty) return 'This field is mandatory';
              return null;
            },
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        tooltip: "Done",
        child: Icon(Icons.done),
        onPressed: () {
          if (_formKey.currentState.validate()) {
            js.context.callMethod('add', [_textController.text]);
            Navigator.pop(context);
          }
        },
      ),
    );
  }
}
这是JS代码 -
const dbRef = firebase.database().ref();

var userList = [];

function read() {
  dbRef.once("value", function (snapshot) {
    userList = [];
    snapshot.forEach(function (childSnapshot) {
      var key = childSnapshot.key;
      var user = childSnapshot.val();
      userList.push({ key, user });
    });
  });
  return userList;
}

function add(user) {
  let newUser = { name: user };

  dbRef.push(newUser, function () {
    console.log("Attendance Record Updated - New Attendee Added");
  });
}
Firebase RT DB 中的数据库结构 -
firebase_RTDB
解析时的数据库结构 -
parsed_DB

调试这段代码真是令人沮丧,因为我不能 print main.dart 的输出如果发生任何错误并抛出异常,则以转编译main.dart.js的形式显示无法读取的文件。
我无法从 read() 取回数据JS文件中的方法到main.dart .怎么做?
一些引用链接 -
https://twitter.com/rodydavis/status/1197564669633978368
https://www.reddit.com/r/FlutterDev/comments/dyd8j5/create_chrome_extension_running_flutter/
https://github.com/rodydavis/dart_pad_ext/

最佳答案

调用callMethod时它返回的正是 javascript 函数返回的内容。它不返回 json 编码的 String就像您的代码当前暗示的那样。因此没有必要对其进行解码。 var obj几乎可以被视为 List<Map<String, dynamic>>目的。这意味着您可以访问列表的第一个对象和 key js.context.callMethod("read")[0]['key'] 的属性(property)在你的 Dart 代码中。您可以删除代码中的所有 json 解码和强制转换,并将其替换为使用此方法访问返回数据的函数,以将其转换为实际的 List<Map<String, dynamic>>你自己。
示例 getAttendees修改:

Future<void> getAttendees() async {
  List<Map<String, dynamic>> toReturn = List();
  js.JsArray obj = js.context.callMethod("read");
  for(js.JsObject eachObj in obj) {
    //For every object in the array, convert it to a `Map` and add to toReturn
    toReturn.add({      
      'key': eachObj['key'],
      'user': eachObj['user'],
    });
  }
  setState(() {
    data = toReturn;
  });
}
如果您出于某种原因发现需要使用 json,则需要在 javascript 端进行编码,尽管我认为没有理由这样做,因为它只会增加复杂性。
当显示 build 中的数据时功能 data.toString()[index]将首先将 javascript 输出转换为 String像这样:"[{key: key}, {key: key}, ...]"然后找到indexString ,而不是 List对象,这可能是您想要的。为此,您首先使用索引 data[index]然后添加您要查找的字段data[index]['key']因为数据是 ListMap s。
与您遇到的问题无关,但一些一般性的 flutter 建议是使用 FutureBuilder而不是您当前显示数据的方法 async功能完成。你的方法几乎可以做到 FutureBuilder会,只是效率稍低一些,可读性较差。
编辑:
办理read作为 Promise :
首先你必须添加 js: ^0.6.2给您的pubspec.yaml依赖关系。
然后必须在代码中的某处添加:
@JS()
library script.js;

import 'package:js/js.dart';
import 'dart:js_util';

@JS()
external dynamic read();
js.JsArray obj = js.context.callMethod("read");应该修改为 await promiseToFuture(read()) .整机getAttendees应修改为:
Future<void> getAttendees() async {
  List<Map<String, dynamic>> toReturn = List();
  dynamic obj = await promiseToFuture(read());
  for(dynamic eachObj in obj) {
    //For every object in the array, convert it to a `Map` and add to toReturn
    toReturn.add({      
      'key': eachObj.key,
      'user': eachObj.user,
    });
  }
  setState(() {
    data = toReturn;
  });
}

关于javascript - 使用js.context.callMethod()调用方法时如何接收数据返回flutter?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62608004/

相关文章:

javascript - jQuery 将日期时间转换为特定格式

firebase - 如何从Flutter到本地云功能服务器进行HTTP POST

flutter - 我如何在 flutter 的 child 体内使用if/else语句?

dart - 在Dart Future中如何处理

flutter - 使用Flutter RadioListTile的多项选择Qns测试应用

javascript - 在 Python 中发送表单请求

javascript - HTML 按钮刷新页面而不是将数据发送到数据库

javascript - promise - 链接解决/拒绝

android - 更新包后 Flutter Android 构建失败。安卓X错误

list - 如何通过模型类传递 boolean 值列表并在有状态的小部件中对其进行初始化?