使用 js.context.callMethod() 调用方法时如何将数据接收回 flutter?
How to receive data back to flutter when calling a method using js.context.callMethod()?
我正在使用 Flutter Web 构建一个 chrome 扩展,这只是一个简单的 ListView
在主屏幕上和基本的 CRUD 操作。我正在使用 dart:js
包从 JS 文件调用方法,该文件在 Firebase 实时数据库上执行一些操作。
通过 add()
方法调用 工作 向数据库添加新条目。读取操作在 JS 文件中也正常工作。
我的主要问题是我应该如何读取数据库信息JSON,解析它并在Flutter中将其显示为ListView
。
这里是 main.dart
和 AddAttendee.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 中的数据库结构 -
解析时的数据库结构-
调试这段代码真是令人沮丧,因为我无法 print
main.dart
的输出,如果发生任何错误并抛出异常,那么它会以转译的形式显示 main.dart.js
无法读取的文件。
我没能从JS文件中的read()
方法取回数据到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
时,它 return 正是 javascript 函数 return 的内容。它不像您的代码当前暗示的那样 return json 编码 String
。因此不需要对其进行解码。 var obj
可以 几乎 被视为 List<Map<String, dynamic>>
对象。这意味着您可以在飞镖代码中使用 js.context.callMethod("read")[0]['key']
访问列表的第一个对象和 key
属性。您可以删除代码中的所有 json 解码和转换,并将其替换为使用此方法访问 returned 数据的函数,以自行将其转换为实际的 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}, ...]"
然后查找String
的 index
,而不是 List
对象,这可能是您想要的。为此,您首先使用索引 、data[index]
,然后添加您要查找的字段 data[index]['key']
,因为数据是 List
的 Map
s.
与您遇到的问题没有直接关系,但一些一般性的建议是在 async
函数完成时使用 FutureBuilder
而不是当前显示数据的方法。您的方法 nearly 做了 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;
});
}
由于 Christopher Moore 对我的问题给出了一个通用的解决方案,我将 post 我使用的精确实现。
数据库结构-
JS代码-
const dbRef = firebase.database().ref();
var userList = [];
async function read() {
await dbRef.once("value", function (snapshot) {
userList = [];
snapshot.forEach(function (childSnapshot) {
var key = childSnapshot.key;
var user = childSnapshot.val();
userList.push({ key, user });
});
//console.log(userList);
});
return userList;
}
JS-Dart 互操作 -
@JS()
library js_interop;
import 'package:js/js.dart';
import 'package:js/js_util.dart';
@JS()
external dynamic read();
class FBOps {
final List<Map<String, dynamic>> toReturn = [];
Map getUserDetails(objMap) {
final Map<String, dynamic> temp = {};
temp['name'] = objMap.user.name;
temp['status'] = objMap.user.status;
return temp;
}
Future<List> getList() async {
dynamic obj = await promiseToFuture(read());
for (dynamic eachObj in obj) {
toReturn.add({
'key': eachObj.key,
'user': getUserDetails(eachObj),
});
}
return toReturn;
}
}
从互操作中获取 Map
-
List<Map> data;
Future<void> getAttendees() async {
await FBOps().getList().then((value) => data = value);
//print(data);
}
我正在使用 Flutter Web 构建一个 chrome 扩展,这只是一个简单的 ListView
在主屏幕上和基本的 CRUD 操作。我正在使用 dart:js
包从 JS 文件调用方法,该文件在 Firebase 实时数据库上执行一些操作。
通过 add()
方法调用 工作 向数据库添加新条目。读取操作在 JS 文件中也正常工作。
我的主要问题是我应该如何读取数据库信息JSON,解析它并在Flutter中将其显示为ListView
。
这里是 main.dart
和 AddAttendee.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 中的数据库结构 -
解析时的数据库结构-
调试这段代码真是令人沮丧,因为我无法 print
main.dart
的输出,如果发生任何错误并抛出异常,那么它会以转译的形式显示 main.dart.js
无法读取的文件。
我没能从JS文件中的read()
方法取回数据到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
时,它 return 正是 javascript 函数 return 的内容。它不像您的代码当前暗示的那样 return json 编码 String
。因此不需要对其进行解码。 var obj
可以 几乎 被视为 List<Map<String, dynamic>>
对象。这意味着您可以在飞镖代码中使用 js.context.callMethod("read")[0]['key']
访问列表的第一个对象和 key
属性。您可以删除代码中的所有 json 解码和转换,并将其替换为使用此方法访问 returned 数据的函数,以自行将其转换为实际的 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}, ...]"
然后查找String
的 index
,而不是 List
对象,这可能是您想要的。为此,您首先使用索引 、data[index]
,然后添加您要查找的字段 data[index]['key']
,因为数据是 List
的 Map
s.
与您遇到的问题没有直接关系,但一些一般性的建议是在 async
函数完成时使用 FutureBuilder
而不是当前显示数据的方法。您的方法 nearly 做了 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;
});
}
由于 Christopher Moore 对我的问题给出了一个通用的解决方案,我将 post 我使用的精确实现。
数据库结构-
JS代码-
const dbRef = firebase.database().ref();
var userList = [];
async function read() {
await dbRef.once("value", function (snapshot) {
userList = [];
snapshot.forEach(function (childSnapshot) {
var key = childSnapshot.key;
var user = childSnapshot.val();
userList.push({ key, user });
});
//console.log(userList);
});
return userList;
}
JS-Dart 互操作 -
@JS()
library js_interop;
import 'package:js/js.dart';
import 'package:js/js_util.dart';
@JS()
external dynamic read();
class FBOps {
final List<Map<String, dynamic>> toReturn = [];
Map getUserDetails(objMap) {
final Map<String, dynamic> temp = {};
temp['name'] = objMap.user.name;
temp['status'] = objMap.user.status;
return temp;
}
Future<List> getList() async {
dynamic obj = await promiseToFuture(read());
for (dynamic eachObj in obj) {
toReturn.add({
'key': eachObj.key,
'user': getUserDetails(eachObj),
});
}
return toReturn;
}
}
从互操作中获取 Map
-
List<Map> data;
Future<void> getAttendees() async {
await FBOps().getList().then((value) => data = value);
//print(data);
}